驱动学习(六)ioctl
linux内核给用户提供了两类系统调用函数:一类是数据操作函数,比如read、write…。 另外一类函数是非数据操作函数,比如ioctl…,用户程序可以用ioctl给底层设备发送指令。
1. ioctl
ioctl是一个系统调用函数,应用程序可以用它给内核发送指令。
int ioctl(int d, int request, ...);
d:文件描述符
request:命令码。
...:可变参数,可有可无,若有,该参数传送内核驱动
返回值:成功为0
2. 命令码
2.1 自定义命令码
2.2 标准命令码
标准命令码(u32)
30-31:数据的控制方向(2bit get/set)
16-29:数据的大小(14bit)
8-15:设备类型(8bit)
0-7:cmd(命令码)(区分命名的顺序序号)
2.2.1 合成标准命令码的宏函数
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \ 方向(左移30位)
((type) << _IOC_TYPESHIFT) | \ 设备类型(左移8位)
((nr) << _IOC_NRSHIFT) | \ 命令码(低8位)
((size) << _IOC_SIZESHIFT)) 数据大小(左移16位)
不同类型的标准命令码合成宏函数
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
3. 测试ioctl
环境 ubuntu 20.04
思路:在test.c中调用ioctl函数发送cmd,在驱动模块中让对应的cmd打印不同的信息
#####ioctl.c
1 #include <linux/module.h> //模块驱动的头文件
2 #include <linux/cdev.h> //设备信息描述的头文件
3 #include <linux/fs.h> //静态申请设备号头文件
4 #include <linux/kdev_t.h> //设备号用到的头文件和宏函数
5 #include <linux/uaccess.h>
6 #include <linux/device.h>
7 #include "common.h"
8
9 #define BUF_SIZE 100
10
11 int major = 0; //主设备号
12 int min = 0; //次设备号
13 int deviceNum = 0; //完整设备号
14 struct cdev* pCdev = NULL; //描述设备信息的结构体
15 char *deviceName = "ioctl";//设备名
16 char buff[BUF_SIZE] = "chrdev-test-2022-7-12";
17 struct class *pClass = NULL; //设备文件类指针
18 int devNum = 2; //设备文件数量
19
20 int testOpen(struct inode *pNode,struct file *pFile)
21 {
22 printk("------into test open------\n");
23 printk("------leave test open------\n");
24 return 0;
25 }
26 int testClose(struct inode *pNode,struct file *pFile)
27 {
28 printk("------into test close------\n");
29 printk("------leave test close------\n");
30 return 0;
31 }
32 ssize_t testRead(struct file *pFile,char __user *buf,size_t count,loff_t *pOffset)
33 {
34 int res = -1;
35 printk("------into testRead------\n");
36 if(count > BUF_SIZE-1)
37 {
38 count = BUF_SIZE - 1;
39 }
40 res = copy_to_user(buf,buff,count);
41 if(res)
42 {
43 printk("copy_to_user error\n");
44 return -EFAULT;
45 }
46 printk("copy_to_user ok\n");
47 printk("\t buff = %s\t\n",buff);
48 printk("------leave testRead------\n");
49 return count;
50 }
51
52 ssize_t testWrite(struct file *pFile,const char __user *buf,size_t count,loff_t *pOffset)
53 {
54 int res = -1;
55 printk("------into testWrite------\n");
56 if(count > BUF_SIZE-1)
57 {
58 count = BUF_SIZE - 1;
59 }
60 res = copy_from_user(buff,buf,count);
61 if(res)
62 {
63 printk("copy_from_user error\n");
64 return -EFAULT;
65 }
66 printk("copy_from_user ok\n");
67 printk("\t buff = %s\t\n",buff);
68 printk("------leave testWrite------\n");
69 return count;
70 }
71
72 long testIoctl(struct file *pFile,unsigned int cmd,unsigned long arg)
73 {
74 switch(cmd)
75 {
76 case TEST_CMD:
77 {
78 printk("test cmd-------arg = %ld \n",arg);
79 }
80 break;
81 case TEST_CMD1:
82 {
83 printk("test cmd1-------arg = %ld \n",arg);
84 }
85 break;
86 case TEST_CMD2:
87 {
88 printk("test cmd2-------arg = %ld \n",arg);
89 }
90 break;
91 default:
92 printk("error cmd \n");
93 }
94 return 0;
95 }
96
97 struct file_operations fp_arr =
98 {
99 .owner = THIS_MODULE,
100 .open = testOpen,
101 .read = testRead,
102 .write = testWrite,
103 .release = testClose,
104 .unlocked_ioctl = testIoctl
105 };
106
107 int driverr_init(void) //模块初始化函数
108 {
109 int res = 0;
110 int i = 0;
111 struct device *pDevTmp = NULL;
112 printk("*********into driver init\n");
113 //动态申请设备号
114 res = alloc_chrdev_region(&deviceNum,min,devNum,deviceName);
115 if(res)
116 {
117 printk("alloc_chrdev_region error\n");
118 return res;
119 }
120 printk("alloc_chrdev_region OK!\n");
121 printk("major = %d minor = %d \n",MAJOR(deviceNum),MINOR(deviceNum));
122 major = MAJOR(deviceNum);
123 //创建设备
124 pCdev = cdev_alloc();
125 if(NULL == pCdev)
126 {
127 printk("cdev_alloc error\n");
128 unregister_chrdev_region(deviceNum,devNum);
129 return -1;
130 }
131 printk("cdev_alloc ok\n");
132 //设备初始化
133 cdev_init(pCdev,&fp_arr);
134 printk("cdev_init ok\n");
135 //设备与设备号关联
136 res = cdev_add(pCdev,deviceNum,devNum);
137 if(res)
138 {
139 printk("cdev_add error\n");
140 cdev_del(pCdev);
141 }
142 printk("cdev_add ok\n");
143 //创建设备文件类
144 pClass = class_create(THIS_MODULE,"ioctl");
145 if(NULL == pClass)
146 {
147 printk("class_create error\n");
148 cdev_del(pCdev);
149 }
150 printk("class_create ok\n");
151 //创建设备文件
152 for(;i < devNum;i++)
153 {
154 pDevTmp = device_create(pClass,NULL,MKDEV(major,i),NULL,"ioctl%d",i);
155 if(IS_ERR(pDevTmp))
156 {
157 printk("device_create error\n");
158 for(i = 0;i < devNum;i++)
159 {
160 device_destroy(pClass,MKDEV(major,i));
161 }
162 class_destroy(pClass);
163 return -2;
164 }
165 }
166 printk("device_create ok\n");
167 printk("*********leave driver init\n");
168 return 0;
169 }
170
171 void driver_clear(void) //模块清除函数
172 {
173 int i = 0;
174 printk("*********into driver clear\n");
175 for(;i < devNum; i++)
176 {
177 device_destroy(pClass,MKDEV(major,i));
178 }
179 class_destroy(pClass);
180 cdev_del(pCdev);
181 unregister_chrdev_region(deviceNum,devNum);
182 printk("*********leave driver clear\n");
183 }
184
185 module_init(driverr_init); //模块加载函数
186 module_exit(driver_clear); //模块卸载函数
187
188
189 MODULE_LICENSE("GPL");
190 MODULE_AUTHOR("cfy");
191 MODULE_ALIAS("liangzai");
192 MODULE_DESCRIPTION("2022-7-12");
#####test.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <sys/ioctl.h>
8 #include "common.h"
9 #define BUF_SIZE 100
10
11 int main()
12 {
13 int fd = open("/dev/ioctl0",O_RDWR);
14 if(fd < 0)
15 {
16 printf("open ioctl0 err\n");
17 return -1;
18 }
19 printf("open ioctl0 ok\n");
20
21 ioctl(fd,TEST_CMD);
22 ioctl(fd,TEST_CMD1,123456);
23 ioctl(fd,TEST_CMD2);
24
25 close(fd);
26 return 0;
27
28
29 }
编译、查看设备文件、查看内核打印
添加设备文件权限
sudo chmod 777 /dev/ioctl0 /dev/ioctl1
执行测试程序,查看内核打印信息
可以看到第一次的cmd没有传参,arg为0;第二次传参了为123456;第三次未传参arg被第二次的赋值为123456,验证完毕。
接下来是驱动互斥。