一. 简介
上一篇文章学习了向设备树文件中添加Led设备节点信息。具体包括 Led的 pinctrl节点与 gpio节点。文章地址如下:
pinctrl子系统与gpio子系统实验-测试设备树文件-CSDN博客
本文继续 pinctrl子系统与gpio子系统实验的开发学习。本文实现 Led设备驱动代码框架。
二. Led驱动框架代码实现
1. 创建 vscode工程
这里我所存放的驱动实验代码在 ubuntu系统下的如下目录:
/home/wangtian/zhengdian_Linux/Linux_Drivers
打开 ubuntu系统,进入如上目录,创建 工程目录 6_gpioled:
进入 6_gpioled目录下,将前面实验 5_dtsled工程中 .vscode目录及其下文件拷贝到该工程下:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers$ cd 6_gpioled/
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/6_gpioled$ cp ../5_dtsled.c/.vscode/ ./ -rf
注意: 拷贝.vscode 目录及其下文件的原因在于,.vscode目录下的文件中设置了 驱动程序可能会调用到的 Linux内核源码的路径(NXP官方)。
拷贝 Makefile文件
将前面实验 5_dtsled工程中Makefile拷贝到该工程下,操作如下:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/6_gpioled$ cp ../5_dtsled.c/Makefile ./
更改 Makefile文件的目标文件名,更改为如下:
obj-m := gpioled.o
创建 gpioled.c文件:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/5_dtsled.c$ touch gpioled.c
2. Led驱动框架代码搭建
将 5_dtsled工程中 dtsled.c文件中头文件拷贝到gpioled.c文件中:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
接下来再重新编写一次 Led驱动框架代码(代码实现其实与 5_dtsled工程的驱动框架是一样的)。
gpioled.c 文件中的驱动框架代码实现如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define GPIOLED_NAME "gpioled"
#define GPIOLED_CNT 1
//Led设备结构体
struct gpio_led {
dev_t devid;
int major;
int minor;
struct cdev cdev;
struct class* class;
struct device* dev;
};
struct gpio_led gpioled;
static int gpioled_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;
return 0;
}
static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
return 0;
}
static int gpioled_close(struct inode *inode, struct file *filp)
{
return 0;
}
const struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = gpioled_open,
.release = gpioled_close,
.write = gpioled_write,
};
/*驱动模块入口函数*/
static int __init gpioled_init(void)
{
int ret = 0;
/*1.注册设备号*/
gpioled.major = 0;
if(gpioled.major) //给出主设备号
{
gpioled.devid = MKDEV(gpioled.major, 0);
ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}
else //未给设备号时,向Linux申请设备号
{
ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("gpioled.major: %d\n", gpioled.major);
printk("gpioled.minor: %d\n", gpioled.minor);
if(ret < 0)
{
printk("apply dev_number failed!\n");
goto apply_devid_failed;
}
/*2.设备初始化 */
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
if(ret < 0)
{
printk("cdev_add failed\n");
goto cdev_add_failed;
}
/*3.自动创建设备节点*/
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if (IS_ERR(gpioled.class)) {
ret = PTR_ERR(gpioled.class);
goto class_create_failed;
}
gpioled.dev = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if (IS_ERR(gpioled.dev)) {
ret = PTR_ERR(gpioled.dev);
goto dev_create_failed;
}
return 0;
dev_create_failed:
class_destroy(gpioled.class);
class_create_failed:
cdev_del(&gpioled.cdev);
cdev_add_failed:
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
apply_devid_failed:
return ret;
}
/*驱动模块出口函数*/
static void __exit gpioled_exit(void)
{
/*1. 删除设备*/
cdev_del(&gpioled.cdev);
/*2. 注销设备号*/
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
//3. 销毁设备
device_destroy(gpioled.class, gpioled.devid);
/*4. 销毁类*/
class_destroy(gpioled.class);
}
/*驱动入口与出口*/
module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL"); //License
MODULE_AUTHOR("WeiWuXian"); //author
三. 编译驱动
ubuntu终端进入 6_gpioled工程根目录下,编译驱动框架代码:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/6_gpioled$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/6_gpioled modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
Building modules, stage 2.
MODPOST 1 modules
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/6_gpioled$
可以看出,驱动模块正常编译。下一篇文章对该驱动框架代码进行测试。