在本实验中,使用misc设备驱动的方式来编写蜂鸣器的驱动,用 platform 来实现总线、设备和驱动,misc 主要负责完成字符设备的创建。
1 修改设备树文件
设备树中的beep设备节点直接使用36.3.2中创建的beep设备节点即可。
2 编写驱动程序
本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/16_beep_misc
创建beep_misc.c文件,具体内容如下:
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <linux/of_gpio.h>
14 #include <linux/platform_device.h>
15 #include <linux/miscdevice.h>
16 #include <asm/mach/map.h>
17 #include <asm/uaccess.h>
18 #include <asm/io.h>
19
20 #define MISCBEEP_NAME "miscbeep" /* 名字 */
21 #define MISCBEEP_MINOR 144 /* 子设备号 */
22 #define BEEPOFF 0 /* 关蜂鸣器 */
23 #define BEEPON 1 /* 开蜂鸣器 */
24
25 /* miscbeep设备结构体 */
26 struct miscbeep_dev{
27 dev_t devid; /* 设备号*/
28 struct cdev cdev; /* cdev */
29 struct class *class; /* 类 */
30 struct device *device; /* 设备*/
31 struct device_node *nd; /* 设备节点 */
32 int beep_gpio; /* beep所使用的GPIO编号 */
33 };
34
35 struct miscbeep_dev miscbeep; /* beep设备 */
36
37 /*
38 * @description : 打开设备
39 * @param - inode : 传递给驱动的inode
40 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
41 * 一般在open的时候将private_data指向设备结构体。
42 * @return : 0 成功;其他 失败
43 */
44 static int miscbeep_open(struct inode *inode, struct file *filp)
45 {
46 filp->private_data = &miscbeep; /* 设置私有数据 */
47 return 0;
48 }
49
50 /*
51 * @description : 向设备写数据
52 * @param - filp : 设备文件,表示打开的文件描述符
53 * @param - buf : 要写给设备写入的数据
54 * @param - cnt : 要写入的数据长度
55 * @param - offt : 相对于文件首地址的偏移
56 * @return : 写入的字节数,如果为负值,表示写入失败
57 */
58 static ssize_t miscbeep_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
59 {
60 int retvalue;
61 unsigned char databuf[1];
62 unsigned char beepstat;
63 struct miscbeep_dev *dev = filp->private_data;
64
65 retvalue = copy_from_user(databuf, buf, cnt);
66 if(retvalue < 0) {
67 printk("kernel write failed!\r\n");
68 return -EFAULT;
69 }
70
71 beepstat = databuf[0]; /* 获取状态值 */
72 if(beepstat == BEEPON) {
73 gpio_set_value(dev->beep_gpio, 1); /* 打开蜂鸣器 */
74 } else if(beepstat == BEEPOFF) {
75 gpio_set_value(dev->beep_gpio, 0); /* 关闭蜂鸣器 */
76 }
77 return 0;
78 }
79
80 /* 设备操作函数 */
81 static struct file_operations miscbeep_fops = {
82 .owner = THIS_MODULE,
83 .open = miscbeep_open,
84 .write = miscbeep_write,
85 };
86
87 /* MISC设备结构体 */
88 static struct miscdevice beep_miscdev = {
89 .minor = MISCBEEP_MINOR,
90 .name = MISCBEEP_NAME,
91 .fops = &miscbeep_fops,
92 };
93
94 /*
95 * @description : flatform驱动的probe函数,当驱动与
96 * 设备匹配以后此函数就会执行
97 * @param - dev : platform设备
98 * @return : 0,成功;其他负值,失败
99 */
100 static int miscbeep_probe(struct platform_device *dev)
101 {
102 int ret = 0;
103
104 printk("beep driver and device was matched!\r\n");
105 /* 设置BEEP所使用的GPIO */
106 /* 1、获取设备节点:beep */
107 miscbeep.nd = of_find_node_by_path("/beep");
108 if(miscbeep.nd == NULL) {
109 printk("beep node not find!\r\n");
110 return -EINVAL;
111 }
112
113 /* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */
114 miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);
115 if(miscbeep.beep_gpio < 0) {
116 printk("can't get beep-gpio");
117 return -EINVAL;
118 }
119
120 /* 3、设置GPIO5_IO01为输出,并且输出低电平,默认关闭BEEP */
121 ret = gpio_direction_output(miscbeep.beep_gpio, 0);
122 if(ret < 0) {
123 printk("can't set gpio!\r\n");
124 }
125
126 /* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备
127 * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可
128 */
129 ret = misc_register(&beep_miscdev);
130 if(ret < 0){
131 printk("misc device register failed!\r\n");
132 return -EFAULT;
133 }
134
135 return 0;
136 }
137
138 /*
139 * @description : platform驱动的remove函数,移除platform驱动
的时候此函数会执行
140 * @param - dev : platform设备
141 * @return : 0,成功;其他负值,失败
142 */
143 static int miscbeep_remove(struct platform_device *dev)
144 {
145 /* 注销设备的时候关闭LED灯 */
146 gpio_set_value(miscbeep.beep_gpio, 1);
147
148 /* 注销misc设备 */
149 misc_deregister(&beep_miscdev);
150 return 0;
151 }
152
153 /* 匹配列表 */
154 static const struct of_device_id beep_of_match[] = {
155 {
.compatible = "beep" },
156 {
/* Sentinel */ }
157 };
158
159 /* platform驱动结构体 */
160 static struct platform_driver beep_driver = {
161 .driver = {
162 .name = "imx6ul-beep", /* 驱动名字,用于和设备匹配 */
163 .of_match_table = beep_of_match, /* 设备树匹配表*/
164 },
165 .probe = miscbeep_probe,
166 .remove = miscbeep_remove,
167 };
168
169 /*
170 * @description : 驱动出口函数
171 * @param : 无
172 * @return : 无
173 */
174 static int __init miscbeep_init(void)
175 {
176 return platform_driver_register(&beep_driver);
177 }
178
179 /*
180 * @description : 驱动出口函数
181 * @param : 无
182 * @return : 无
183 */
184 static void __exit miscbeep_exit(void)
185 {
186 platform_driver_unregister(&beep_driver);
187 }
188
189 module_init(miscbeep_init);
190 module_exit(miscbeep_exit);
191 MODULE_LICENSE("GPL");
192 MODULE_AUTHOR("topeet");
第 88~92 行,misc 设备结构体 beep_miscdev,第 89 行设置子设备号为 144,第 90 行设置设备名字为“miscbeep”,这样当系统启动以后就会在/dev/目录下存在一个名为“miscbeep”的设备文件。第 91 行,设置 misc 设备的操作函数集合,为 file_operations 类型。
第 100~136 行,platform 框架的 probe 函数,当驱动与设备匹配以后此函数就会执行,首先在此函数中初始化 BEEP 所使用的 IO。最后在 129 行通过 misc_register 函数向 Linux 内核注册misc 设备,也就是前面定义的 beep_miscdev。
第 143~151 行,platform 框架的 remove 函数,在此函数中调用 misc_deregister 函数来注销misc 设备。