Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长
学习是一个不断重复的过程。只有不断的使用、修正,才能越记越牢。将学习到的新的知识点应用到以往的项目经验中,才能不断提升自我,长此以往,温故知新,才能由量变产生质变。
本节笔记主要学习LinuxMISC驱动试验。除了最基本的简介知识点外,主要重点在于试验程序的编写。
本节的思维导图如下:
一、MISC设备驱动简介
MISC 驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动。
所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。
MISC 设备会自动创建 cdev,向 Linux 注册一个 miscdevice 设备。
57 struct miscdevice {
58 int minor; /* 子设备号 */
59 const char *name; /* 设备名字 */
60 const struct file_operations *fops; /* 设备操作集 */
61 struct list_head list;
62 struct device *parent;
63 struct device *this_device;
64 const struct attribute_group **groups;
65 const char *nodename;
66 umode_t mode;
67 };
minor 表示子设备号, MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号。
name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name
的设备文件。 fops 就是字符设备的操作集合, MISC 设备驱动最终是需要使用用户提供的 fops
操作集合。
使用 misc_register 函数向系统中注册一个 MISC 设备。
int misc_register(struct miscdevice * misc)
卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备。
int misc_deregister(struct miscdevice *misc)
二、原理图分析
三、试验程序编写
1、修改设备树
1 pinctrl_beep: beepgrp {
2 fsl,pins = <
3 MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 /* beep */
4 >;
5 };
2、beep驱动程序编写
29 #define MISCBEEP_NAME "miscbeep" /* 名字 */
30 #define MISCBEEP_MINOR 144 /* 子设备号 */
31 #define BEEPOFF 0 /* 关蜂鸣器 */
32 #define BEEPON 1 /* 开蜂鸣器 */
33
34 /* miscbeep 设备结构体 */
35 struct miscbeep_dev{
36 dev_t devid; /* 设备号 */
37 struct cdev cdev; /* cdev */
38 struct class *class; /* 类 */
39 struct device *device; /* 设备 */
40 struct device_node *nd; /* 设备节点 */
41 int beep_gpio; /* beep 所使用的 GPIO 编号 */
42 };
43
44 struct miscbeep_dev miscbeep; /* beep 设备 */
45
46 /*
47 * @description : 打开设备
48 * @param – inode : 传递给驱动的 inode
49 * @param - filp : 设备文件, file 结构体有个叫做 private_data 的成员变量
50 * 一般在 open 的时候将 private_data 指向设备结构体。
51 * @return : 0 成功;其他 失败
52 */
53 static int miscbeep_open(struct inode *inode, struct file *filp)
54 {
55 filp->private_data = &miscbeep; /* 设置私有数据 */
56 return 0;
57 }
58
59 /*
60 * @description : 向设备写数据
61 * @param - filp : 设备文件,表示打开的文件描述符
62 * @param - buf : 要写给设备写入的数据
63 * @param - cnt : 要写入的数据长度
64 * @param - offt : 相对于文件首地址的偏移
65 * @return : 写入的字节数,如果为负值,表示写入失败
66 */
67 static ssize_t miscbeep_write(struct file *filp,
const char __user *buf, size_t cnt, loff_t *offt)
68 {
69 int retvalue;
70 unsigned char databuf[1];
71 unsigned char beepstat;
72 struct miscbeep_dev *dev = filp->private_data;
73
74 retvalue = copy_from_user(databuf, buf, cnt);
75 if(retvalue < 0) {
76 printk("kernel write failed!\r\n");
77 return -EFAULT;
78 }
79
80 beepstat = databuf[0]; /* 获取状态值 */
81 if(beepstat == BEEPON) {
82 gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */
83 } else if(beepstat == BEEPOFF) {
84 gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */
85 }
86 return 0;
87 }
88
89 /* 设备操作函数 */
90 static struct file_operations miscbeep_fops = {
91 .owner = THIS_MODULE,
92 .open = miscbeep_open,
93 .write = miscbeep_write,
94 };
标准的字符设备驱动。
96 /* MISC 设备结构体 */
97 static struct miscdevice beep_miscdev = {
98 .minor = MISCBEEP_MINOR,
99 .name = MISCBEEP_NAME,
100 .fops = &miscbeep_fops,
101 };
MISC 设备 beep_miscdev,第 98 行设置子设备号为 144,第 99 行设置设备名字为“ miscbeep”,这样当系统启动以后就会在/dev/目录下存在一个名为“ miscbeep”的设备文件。第 100 行,设置 MISC 设备的操作函数集合,为 file_operations 类型。
103 /*
104 * @description : flatform 驱动的 probe 函数,当驱动与
105 * 设备匹配以后此函数就会执行
106 * @param - dev : platform 设备
107 * @return : 0,成功;其他负值,失败
108 */
109 static int miscbeep_probe(struct platform_device *dev)
110 {
111 int ret = 0;
112
113 printk("beep driver and device was matched!\r\n");
114 /* 设置 BEEP 所使用的 GPIO */
115 /* 1、获取设备节点: beep */
116 miscbeep.nd = of_find_node_by_path("/beep");
117 if(miscbeep.nd == NULL) {
118 printk("beep node not find!\r\n");
119 return -EINVAL;
120 }
121
122 /* 2、 获取设备树中的 gpio 属性,得到 BEEP 所使用的 BEEP 编号 */
123 miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio",
0);
124 if(miscbeep.beep_gpio < 0) {
125 printk("can't get beep-gpio");
126 return -EINVAL;
127 }
128
129 /* 3、设置 GPIO5_IO01 为输出,并且输出高电平,默认关闭 BEEP */
130 ret = gpio_direction_output(miscbeep.beep_gpio, 1);
131 if(ret < 0) {
132 printk("can't set gpio!\r\n");
133 }
134
135 /* 一般情况下会注册对应的字符设备,但是这里我们使用 MISC 设备
136 * 所以我们不需要自己注册字符设备驱动,只需要注册 misc 设备驱动即可
137 */
138 ret = misc_register(&beep_miscdev);
139 if(ret < 0){
140 printk("misc device register failed!\r\n");
141 return -EFAULT;
142 }
143
144 return 0;
145 }
platform 框架的 probe 函数,当驱动与设备匹配以后此函数就会执行,首先在此函数中初始化 BEEP 所使用的 IO。最后在 138 行通过 misc_register 函数向 Linux 内核注册MISC 设备,也就是前面定义的 beep_miscdev。
147 /*
148 * @description : remove 函数,移除 platform 驱动的时候此函数会执行
149 * @param - dev : platform 设备
150 * @return : 0,成功;其他负值,失败
151 */
152 static int miscbeep_remove(struct platform_device *dev)
153 {
154 /* 注销设备的时候关闭 LED 灯 */
155 gpio_set_value(miscbeep.beep_gpio, 1);
156
157 /* 注销 misc 设备驱动 */
158 misc_deregister(&beep_miscdev);
159 return 0;
160 }
platform 框架的 remove 函数,在此函数中调用 misc_deregister 函数来注销
MISC 设备。
162 /* 匹配列表 */
163 static const struct of_device_id beep_of_match[] = {
164 { .compatible = "atkalpha-beep" },
165 { /* Sentinel */ }
166 };
167
168 /* platform 驱动结构体 */
169 static struct platform_driver beep_driver = {
170 .driver = {
171 .name = "imx6ul-beep", /* 驱动名字 */
172 .of_match_table = beep_of_match, /* 设备树匹配表 */
173 },
174 .probe = miscbeep_probe,
175 .remove = miscbeep_remove,
176 };
177
178 /*
179 * @description : 驱动入口函数
180 * @param : 无
181 * @return : 无
182 */
183 static int __init miscbeep_init(void)
184 {
185 return platform_driver_register(&beep_driver);
186 }
187
188 /*
189 * @description : 驱动出口函数
190 * @param : 无
191 * @return : 无
192 */
193 static void __exit miscbeep_exit(void)
194 {
195 platform_driver_unregister(&beep_driver);
196 }
标准的 platform 驱动。
3、编写测试APP
20 #define BEEPOFF 0
21 #define BEEPON 1
22
23 /*
24 * @description : main 主程序
25 * @param - argc : argv 数组元素个数
26 * @param - argv : 具体参数
27 * @return : 0 成功;其他 失败
28 */
29 int main(int argc, char *argv[])
30 {
31 int fd, retvalue;
32 char *filename;
33 unsigned char databuf[1];
34
35 if(argc != 3){
36 printf("Error Usage!\r\n");
37 return -1;
38 }
39
40 filename = argv[1];
41 fd = open(filename, O_RDWR); /* 打开 beep 驱动 */
42 if(fd < 0){
43 printf("file %s open failed!\r\n", argv[1]);
44 return -1;
45 }
46
47 databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
48 retvalue = write(fd, databuf, sizeof(databuf));
49 if(retvalue < 0){
50 printf("BEEP Control Failed!\r\n");
51 close(fd);
52 return -1;
53 }
54
55 retvalue = close(fd); /* 关闭文件 */
56 if(retvalue < 0){
57 printf("file %s close failed!\r\n", argv[1]);
58 return -1;
59 }
60 return 0;
61 }
四、运行测试
1、编译驱动程序和测试APP
obj-m := miscbeep.o
make -j32
编译成功以后就会生成一个名为“ miscbeep.ko”的驱动模块文件。
arm-linux-gnueabihf-gcc miscbeepApp.c -o miscbeepApp
编译成功以后就会生成 miscbeepApp 这个应用程序。
2、运行测试
depmod //第一次加载驱动的时候需要运行此命令
modprobe miscbeep.ko //加载设备模块
当驱动模块加载成功以后我们可以在/sys/class/misc 这个目录下看到一个名为“ miscbeep”
的子目录。
输入ls /dev/miscbeep -l命令查看主次设备号。
输入如下命令打开 BEEP:
./miscbeepApp /dev/miscbeep 1 //打开 BEEP
在输入如下命令关闭 LED 灯:
./miscbeepApp /dev/miscbeep 0 //关闭 BEEP
观察一下 BEEP 能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的
话输入如下命令即可:rmmod miscbeep.ko
五、总结
本节笔记主要学习LinuxMISC驱动试验。除了最基本的简介知识点外,主要重点在于试验程序的编写。
本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。