Linux学习第31天:Linux MISC 驱动实验:温故知新-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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驱动试验。除了最基本的简介知识点外主要重点在于试验程序的编写。


本文为参考正点原子开发板配套教程整理而得仅用于学习交流使用不得用于商业用途。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux