正点原子【第四期】手把手教你学 Linux之驱动开发篇-01

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

学习目的

了解驱动开发和应用开发的过程具有一定的基础就行

第一讲linux驱动开发与裸机开发区别

刚开始听不懂很正常等之后学了一点你就会知道它说啥了

 第二讲字符设备驱动开发基础

 字符设备驱动是最简单的块设备是最难的

 驱动是离不开应用的但是两个是不同的文件而我们在裸机单片机开发中驱动和应用是放在一起的直接一起编译的而linux驱动和应用是完全分开的模块

使用应用层的函数调用驱动对应的函数 

 

 

 重点对于字符设备驱动开发重点编写应用程序对应的open、close、read、write函数

 

第三讲我的第一个Linux驱动

字符设备驱动程序的编写其实就是为了实现下面结构体中的函数选择性实现 

 

驱动模块的加载与卸载 

针对于驱动程序我们可以把驱动程序烧进内核或者编译成模块烧进内核的话会很麻烦因为你需要重新把内核烧录至开发板中因此我们使用的方法是把驱动程序编译成模块而把驱动程序编译成模块就需要在驱动程序中编写模块入口函数和出口函数然后我们在外面执行命令就可以把驱动程序编译成.so模块了 然后把.so通过拷贝到nfs的文件夹中通过网络传输.so文件

 模块入口和出口函数编写

当你执行注册模块命令的时候注册模块内的函数就会被执行chrdevbase_init),同理删除模块命令执行出口函数

 内核里的打印函数printk函数--内核区

而printf是在用户区使用的函数

加载驱动有什么用呢就是为了注册字符设备只有注册到了一个字符设备你才能开始编写open、write、read这些函数

注册与注销字符设备

 register_chrdev的缺点是它会把主设备号下面的次设备号全部使用说明空调只能有一种所以这种很浪费所以这个函数在后面的话我们不会使用之所以讲这个函数只是单纯的告诉我们字符设备开发流程而已。

主设备号的赋值需要使用没有使用的主设备号后面会有一个函数直接随机分配一个没有用的主设备号


static struct file_operations test_fops;

/* 驱动入口函数 */
static int __init xxx_init(void)
 {
/* 入口函数具体内容 *


int retvalue = 0;

/* 注册字符设备驱动 */
 retvalue = register_chrdev(200, "chrtest", &test_fops);
 if(retvalue < 0){
 /* 字符设备注册失败,自行处理 */
}
 return 0;
 }
16
17 /* 驱动出口函数 */
18 static void __exit xxx_exit(void)
19 {
20 /* 注销字符设备驱动 */
21 unregister_chrdev(200, "chrtest");
22 }
23
24 /* 将上面两个函数指定为驱动的入口和出口函数 */
25 module_init(xxx_init);
26 module_exit(xxx_exit);

主设备号表示一类设备而次设备就是主设备中的一个设备比如主设备是空调那么次设备就是格力空调、美的空调等

完整的驱动代码

驱动加载、注册设备、完成open等函数

#define CHRDEVBASE_MAJOR 200 /* 主设备号 */
19 #define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */
20
21 static char readbuf[100]; /* 读缓冲区 */
22 static char writebuf[100]; /* 写缓冲区 */
23 static char kerneldata[] = {"kernel data!"};
24
25 
32 static int chrdevbase_open(struct inode *inode, struct file *filp)
33 {
34 //printk("chrdevbase open!\r\n");
35 return 0;
36 }
37
38 
46 static ssize_t chrdevbase_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
47 {
48 int retvalue = 0;
49
50 /* 向用户空间发送数据 */
51 memcpy(readbuf, kerneldata, sizeof(kerneldata));
52 retvalue = copy_to_user(buf, readbuf, cnt);
53 if(retvalue == 0){
54 printk("kernel senddata ok!\r\n");
55 }else{
56 printk("kernel senddata failed!\r\n");
57 }
58
59 //printk("chrdevbase read!\r\n");
60 return 0;
61 }
62
63 
71 static ssize_t chrdevbase_write(struct file *filp,
const char __user *buf,
size_t cnt, loff_t *offt)
72 {
73 int retvalue = 0;
74 /* 接收用户空间传递给内核的数据并且打印出来 */
75 retvalue = copy_from_user(writebuf, buf, cnt);
76 if(retvalue == 0){
77 printk("kernel recevdata:%s\r\n", writebuf);
78 }else{
79 printk("kernel recevdata failed!\r\n");
80 }
81
82 //printk("chrdevbase write!\r\n");
83 return 0;
84 }
8
91 static int chrdevbase_release(struct inode *inode,
struct file *filp)
92 {
93 //printk("chrdevbase release \r\n");
94 return 0;
95 }
96
97 /*
98 * 设备操作函数结构体
99 */
100 static struct file_operations chrdevbase_fops = {
101 .owner = THIS_MODULE,
102 .open = chrdevbase_open,
103 .read = chrdevbase_read,
104 .write = chrdevbase_write,
105 .release = chrdevbase_release,
106 };
107
108 /*
109 * @description : 驱动入口函数
110 * @param : 无
111 * @return : 0 成功;其他 失败
112 */
113 static int __init chrdevbase_init(void)
114 {
115 int retvalue = 0;
116
117 /* 注册字符设备驱动 */
118 retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME,
&chrdevbase_fops);
119 if(retvalue < 0){
120 printk("chrdevbase driver register failed\r\n");
121 }
122 printk("chrdevbase_init()\r\n");
123 return 0;
124 }
125
126 /*
* @description : 驱动出口函数
128 * @param : 无
129 * @return : 无
130 */
131 static void __exit chrdevbase_exit(void)
132 {
133 /* 注销字符设备驱动 */
134 unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
135 printk("chrdevbase_exit()\r\n");
136 }
137
138 /*
139 * 将上面两个函数指定为驱动的入口和出口函数
140 */
141 module_init(chrdevbase_init);
142 module_exit(chrdevbase_exit);
143
144 /*
145 * LICENSE 和作者信息
146 */
147 MODULE_LICENSE("GPL");
148 MODULE_AUTHOR("zuozhongkai");

编写测试 APP--应用开发

static char usrdata[] = {"usr data!"};
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 char readbuf[100], writebuf[100];
34
35 if(argc != 3){
36 printf("Error Usage!\r\n");
37 return -1;
38 }
39
40 filename = argv[1];
41
42 /* 打开驱动文件 */
43 fd = open(filename, O_RDWR);
44 if(fd < 0){
45 printf("Can't open file %s\r\n", filename);
46 return -1;
47 }
48
49 if(atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */
50 retvalue = read(fd, readbuf, 50);
51 if(retvalue < 0){
52 printf("read file %s failed!\r\n", filename);
53 }else{
54 /* 读取成功打印出读取成功的数据 */
55 printf("read data:%s\r\n",readbuf);
56 }
57 }
if(atoi(argv[2]) == 2){
60 /* 向设备驱动写数据 */
61 memcpy(writebuf, usrdata, sizeof(usrdata));
62 retvalue = write(fd, writebuf, 50);
63 if(retvalue < 0){
64 printf("write file %s failed!\r\n", filename);
65 }
66 }
67
68 /* 关闭设备 */
69 retvalue = close(fd);
70 if(retvalue < 0){
71 printf("Can't close file %s\r\n", filename);
72 return -1;
73 }
74
75 return 0;
76 }

创建设备节点文件

驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件应用程序就是通过操作这个设备节点文件来完成对具体设备的操作
然后使用上面的app对chrdevbase 设备操作测试处理
 

开发流程

驱动模块加载、设备注册、函数内部编写、应用程序编写测试

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