驱动开发之read/write:
系统中一切的读写都是站在用户空间的角度来考虑(把你自己当做用户空间)
什么是输入/读?数据从内核空间流向用户空间
什么是输出/写?数据从用户空间流向内核空间
read:
应用层:
ssize_t read(int fd, void *buf, size_t count);
参数1:文件描述符
参数2:存放读取到的数据的空间首地址
参数3:空间大小(不是读到的数据大小)
返回值:成功读取到的字节数
驱动层:
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
参数1:
参数2:应用层read函数的参数2
参数3:应用层read函数的参数3
参数4:文件指针偏移量
返回值:正确读取的字节数
驱动层中实现读功能:
static inline long copy_to_user(void __user *to,const void *from, unsigned long n);
参数1:用户空间缓存区首地址
参数2:内核空间的缓存区首地址
参数3:实际拷贝的字节数
返回值:0成功,非0出错
write:
应用层:
ssize_t write(int fd, const void *buf, size_t count);
参数1:
参数2:存放数据的空间首地址
参数3:实际写的字节数
驱动层:
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
参数1:
参数2:应用层write函数的参数2
参数3:应用层write函数的参数3
参数4:文件指针偏移量
返回值:正确写入的字节数
驱动层中实现写功能:
static inline long copy_from_user(void *to,const void __user * from, unsigned long n)
参数1:内核空间的缓存区首地址
参数2:用户空间缓存区首地址
参数3:实际拷贝的字节数
返回值:0成功,非0出错
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd; 9 10 fd = open("/dev/demo",O_RDWR); 11 if(fd == -1) 12 { 13 perror("open"); 14 return -1; 15 } 16 17 char buf[64]; 18 read(fd,buf,sizeof(buf)); 19 printf("%s ",buf); 20 21 write(fd,"I am from user",15); 22 close(fd); 23 return 0; 24 }
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/uaccess.h> MODULE_LICENSE("GPL"); int major; struct class *cls; struct device *devs; char krbuf[1024] = "I am from kernel"; char kwbuf[1024]; int demo_open(struct inode *inode,struct file *filp) { return 0; } int demo_close(struct inode *inode,struct file *filp) { return 0; } ssize_t demo_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off) { // printk("demo_read "); // strncpy(ubuf,krbuf,strlen(krbuf) + 1); // return strlen(krbuf) + 1; int ret; ssize_t n; if(strlen(krbuf) + 1 > size) n = size; else n = strlen(krbuf) + 1; ret = copy_to_user(ubuf,krbuf,n); if(ret != 0) { return -EFAULT; } return n; } ssize_t demo_write(struct file *filp,const char __user *ubuf,size_t size,loff_t *off) { ssize_t n; int ret; if(size > sizeof(kwbuf)) n = sizeof(kwbuf); else n = size; ret = copy_from_user(kwbuf,ubuf,n); if(ret != 0) return -EFAULT; printk("%s ",kwbuf); return n; } struct file_operations fops = { .owner = THIS_MODULE, .open = demo_open, .read = demo_read, .write = demo_write, .release = demo_close, }; int demo_init(void) { major = register_chrdev(0,"demo",&fops); cls = class_create(THIS_MODULE,"demo"); devs = device_create(cls,NULL,MKDEV(major,0),NULL,"demo"); return 0; } module_init(demo_init); void demo_exit(void) { device_destroy(cls,MKDEV(major,0)); class_destroy(cls); unregister_chrdev(major,"demo"); return; } module_exit(demo_exit);
分支匹配函数:
应用层:
int ioctl(int fd, int request, ...);
参数1:文件描述符
参数2:命令——用来和驱动中的某个分支匹配
参数3:可以没有参数
可以有参数(只能有一个),有参数时可以传递变量名或者变量地址,不能是常量。
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
参数1:
参数2:接收应用层ioctl的参数2
参数3:如果应用层参数3没有传参,此参数没有值
如果应用层参数3传递的是变量名,此参数接收了变量的内容
如果应用层参数3传递的是变量的地址,此参数接收的就是变量地址
命令的封装方法:
命令本身是一个32位无符号整数,这32位被分成了4个部分(方向,数据类型大小,幻数,序号)
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOC(dir,type,nr,size)
(((dir) << _IOC_DIRSHIFT)
((type) << _IOC_TYPESHIFT)
((nr) << _IOC_NRSHIFT)
((size) << _IOC_SIZESHIFT))
dir代表方向
type代表幻数
nr代表序号
size代表数据类型大小
#define _IOC_TYPESHIFT 8
#define _IOC_SIZESHIFT 16
#define _IOC_DIRSHIFT 30
命令 = 方向 << 30 | 数据类型大小 << 16 | 幻数 << 8 | 序号 << 0
方向占2位:无读写数据、只读数据、只写数据、读写数据
数据类型大小占14位:
幻数占8位:如何选择幻数必须查看Documetation/ioctl/ioctl-number.txt
序号占8位:
命令的封装需要调用:
_IO(幻数,序号)
_IOR(幻数,序号,数据类型)
_IOW(幻数,序号,数据类型)
_IOWR(幻数,序号,数据类型)
ioctl代码:
1:
1 struct test 2 { 3 int a; 4 char b; 5 }; 6 7 #define DEMO_CMD1 _IO('x',0) 8 #define DEMO_CMD2 _IO('x',1) 9 #define DEMO_CMD3 _IOW('x',2,int) 10 #define DEMO_CMD4 _IOW('x',3,int) 11 #define DEMO_CMD5 _IOW('x',4,struct test)
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 #include "head.h" 7 8 struct test t = { 9 .a = 100, 10 .b = 'w', 11 }; 12 13 int main(int argc, const char *argv[]) 14 { 15 int fd; 16 17 fd = open("/dev/demo",O_RDWR); 18 if(fd == -1) 19 { 20 perror("open"); 21 return -1; 22 } 23 24 // ioctl(fd,1); 25 // ioctl(fd,2); 26 27 ioctl(fd,DEMO_CMD1); 28 ioctl(fd,DEMO_CMD2); 29 30 int a = 10; 31 ioctl(fd,DEMO_CMD3,a); 32 ioctl(fd,DEMO_CMD4,&a); 33 ioctl(fd,DEMO_CMD5,&t); 34 close(fd); 35 return 0; 36 }
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/fs.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include "head.h" 7 8 MODULE_LICENSE("GPL"); 9 10 int major; 11 struct class *cls; 12 struct device *devs; 13 14 int demo_open(struct inode *inode,struct file *filp) 15 { 16 return 0; 17 } 18 19 long demo_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) 20 { 21 int ret; 22 int a; 23 struct test t; 24 switch(cmd) 25 { 26 #if 0 27 case 1: 28 printk("first cmd "); 29 break; 30 case 2: 31 printk("second cmd "); 32 break; 33 #endif 34 case DEMO_CMD1: 35 printk("first cmd "); 36 break; 37 case DEMO_CMD2: 38 printk("second cmd "); 39 break; 40 case DEMO_CMD3: 41 printk("%ld ",arg); 42 break; 43 case DEMO_CMD4: 44 a = *((int *)arg); 45 printk("%d ",a); 46 break; 47 case DEMO_CMD5: 48 ret = copy_from_user(&t,(struct test *)arg,sizeof(struct test)); 49 printk("%d,%c ",t.a,t.b); 50 break; 51 52 } 53 return 0; 54 } 55 56 int demo_close(struct inode *inode,struct file *filp) 57 { 58 return 0; 59 } 60 61 struct file_operations fops = { 62 .owner = THIS_MODULE, 63 .open = demo_open, 64 .unlocked_ioctl = demo_ioctl, 65 .release = demo_close, 66 }; 67 68 int demo_init(void) 69 { 70 major = register_chrdev(0,"demo",&fops); 71 72 cls = class_create(THIS_MODULE,"demo"); 73 74 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"demo"); 75 76 return 0; 77 } 78 module_init(demo_init); 79 80 void demo_exit(void) 81 { 82 device_destroy(cls,MKDEV(major,0)); 83 class_destroy(cls); 84 unregister_chrdev(major,"demo"); 85 return; 86 } 87 module_exit(demo_exit);
2.led:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 #include "head.h" 7 8 int main(int argc, const char *argv[]) 9 { 10 int fd; 11 12 fd = open("/dev/led",O_RDWR); 13 14 while(1) 15 { 16 ioctl(fd,LED2_ON); 17 sleep(1); 18 ioctl(fd,LED3_ON); 19 sleep(1); 20 ioctl(fd,LED1_ON); 21 sleep(1); 22 ioctl(fd,LED4_ON); 23 sleep(1); 24 25 ioctl(fd,LED4_OFF); 26 sleep(1); 27 ioctl(fd,LED3_OFF); 28 sleep(1); 29 ioctl(fd,LED1_OFF); 30 sleep(1); 31 ioctl(fd,LED2_OFF); 32 sleep(1); 33 } 34 return 0; 35 }
1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/fs.h> 4 #include <linux/device.h> 5 #include <asm/io.h> 6 #include "head.h" 7 8 #define GPX2CON 0x11000c40 9 #define GPX1CON 0x11000c20 10 #define GPF3CON 0x114001e0 11 12 unsigned int *gpx2con; 13 unsigned int *gpx2dat; 14 unsigned int *gpx1con; 15 unsigned int *gpx1dat; 16 unsigned int *gpf3con; 17 unsigned int *gpf3dat; 18 19 int fs4412_led_major; 20 struct class *cls; 21 struct device *devs; 22 23 int fs4412_led_open(struct inode *inode,struct file *filp) 24 { 25 return 0; 26 } 27 28 long fs4412_led_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) 29 { 30 switch(cmd) 31 { 32 case LED2_ON: 33 writel((readl(gpx2dat) & ~(1 << 7)) | 1 << 7,gpx2dat); 34 break; 35 case LED2_OFF: 36 writel((readl(gpx2dat) & ~(1 << 7)),gpx2dat); 37 break; 38 case LED1_ON: 39 writel((readl(gpx1dat) & ~(1 << 0)) | 1 << 0,gpx1dat); 40 break; 41 case LED1_OFF: 42 writel((readl(gpx1dat) & ~(1 << 0)),gpx1dat); 43 break; 44 case LED3_ON: 45 writel((readl(gpf3dat) & ~(1 << 4)) | 1 << 4,gpf3dat); 46 break; 47 case LED3_OFF: 48 writel((readl(gpf3dat) & ~(1 << 4)),gpf3dat); 49 break; 50 case LED4_ON: 51 writel((readl(gpf3dat) & ~(1 << 5)) | 1 << 5,gpf3dat); 52 break; 53 case LED4_OFF: 54 writel((readl(gpf3dat) & ~(1 << 5)),gpf3dat); 55 break; 56 } 57 return 0; 58 } 59 60 struct file_operations fs4412_led_ops = { 61 .owner = THIS_MODULE, 62 .open = fs4412_led_open, 63 .unlocked_ioctl = fs4412_led_ioctl, 64 }; 65 66 int __init fs4412_led_init(void) 67 { 68 fs4412_led_major = register_chrdev(0,"led",&fs4412_led_ops); 69 cls = class_create(THIS_MODULE,"led"); 70 devs = device_create(cls,NULL,MKDEV(fs4412_led_major,0),NULL,"led"); 71 72 gpx2con = ioremap(GPX2CON,4); 73 gpx2dat = gpx2con + 1; 74 75 gpx1con = ioremap(GPX1CON,4); 76 gpx1dat = gpx1con + 1; 77 78 gpf3con = ioremap(GPF3CON,4); 79 gpf3dat = gpf3con + 1; 80 81 writel((readl(gpx2con) & ~(0xf << 28)) | 1 << 28,gpx2con); 82 writel((readl(gpx1con) & ~(0xf << 0)) | 1 << 0,gpx1con); 83 writel((readl(gpf3con) & ~(0xff << 16)) | 0x11 << 16,gpf3con); 84 85 return 0; 86 } 87 module_init(fs4412_led_init); 88 89 void __exit fs4412_led_exit(void) 90 { 91 device_destroy(cls,MKDEV(fs4412_led_major,0)); 92 class_destroy(cls); 93 unregister_chrdev(fs4412_led_major,"led"); 94 return; 95 } 96 module_exit(fs4412_led_exit); 97 MODULE_LICENSE("GPL");
3.资源的竞争
1 #define LED_ON _IO('x',0) 2 #define LED_OFF _IO('x',1)
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 #include "head.h" 7 8 int main(int argc, const char *argv[]) 9 { 10 int fd; 11 12 fd = open("/dev/led0",O_RDWR); 13 14 while(1) 15 { 16 ioctl(fd,LED_ON); 17 sleep(1); 18 19 ioctl(fd,LED_OFF); 20 sleep(1); 21 } 22 return 0; 23 }
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 #include "head.h" 7 8 int main(int argc, const char *argv[]) 9 { 10 int fd; 11 12 fd = open("/dev/led1",O_RDWR); 13 14 while(1) 15 { 16 ioctl(fd,LED_ON); 17 sleep(1); 18 19 ioctl(fd,LED_OFF); 20 sleep(1); 21 } 22 return 0; 23 }
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 #include "head.h" 7 8 int main(int argc, const char *argv[]) 9 { 10 int fd; 11 12 fd = open("/dev/led2",O_RDWR); 13 14 while(1) 15 { 16 ioctl(fd,LED_ON); 17 sleep(1); 18 19 ioctl(fd,LED_OFF); 20 sleep(1); 21 } 22 return 0; 23 }
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/ioctl.h> 6 #include "head.h" 7 8 int main(int argc, const char *argv[]) 9 { 10 int fd; 11 12 fd = open("/dev/led3",O_RDWR); 13 14 while(1) 15 { 16 ioctl(fd,LED_ON); 17 sleep(1); 18 19 ioctl(fd,LED_OFF); 20 sleep(1); 21 } 22 return 0; 23 }
1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/fs.h> 4 #include <linux/device.h> 5 #include <asm/io.h> 6 #include "head.h" 7 8 #define GPX2CON 0x11000c40 9 #define GPX1CON 0x11000c20 10 #define GPF3CON 0x114001e0 11 12 unsigned int *gpx2con; 13 unsigned int *gpx2dat; 14 unsigned int *gpx1con; 15 unsigned int *gpx1dat; 16 unsigned int *gpf3con; 17 unsigned int *gpf3dat; 18 19 int fs4412_led_major; 20 struct class *cls; 21 struct device *devs; 22 23 int fs4412_led_open(struct inode *inode,struct file *filp) 24 { 25 int num; 26 num = iminor(inode);//获取次设备号 27 filp->private_data = (void *)num; 28 return 0; 29 } 30 31 void fs4412_led_on(int num) 32 { 33 switch(num) 34 { 35 case 0: 36 //点亮地一个灯 37 break; 38 case 1: 39 break; 40 case 2: 41 break; 42 case 3: 43 break; 44 } 45 } 46 47 void fs4412_led_off(int num) 48 { 49 switch(num) 50 { 51 case 0: 52 //关闭地一个灯 53 break; 54 case 1: 55 break; 56 case 2: 57 break; 58 case 3: 59 break; 60 } 61 } 62 63 64 long fs4412_led_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) 65 { 66 int num; 67 num = (int)filp->private_data; 68 switch(cmd) 69 { 70 case LED_ON: 71 fs4412_led_on(num); 72 break; 73 case LED_OFF: 74 fs4412_led_off(num); 75 break; 76 #if 0 77 case LED2_ON: 78 writel((readl(gpx2dat) & ~(1 << 7)) | 1 << 7,gpx2dat); 79 break; 80 case LED2_OFF: 81 writel((readl(gpx2dat) & ~(1 << 7)),gpx2dat); 82 break; 83 case LED1_ON: 84 writel((readl(gpx1dat) & ~(1 << 0)) | 1 << 0,gpx1dat); 85 break; 86 case LED1_OFF: 87 writel((readl(gpx1dat) & ~(1 << 0)),gpx1dat); 88 break; 89 case LED3_ON: 90 writel((readl(gpf3dat) & ~(1 << 4)) | 1 << 4,gpf3dat); 91 break; 92 case LED3_OFF: 93 writel((readl(gpf3dat) & ~(1 << 4)),gpf3dat); 94 break; 95 case LED4_ON: 96 writel((readl(gpf3dat) & ~(1 << 5)) | 1 << 5,gpf3dat); 97 break; 98 case LED4_OFF: 99 writel((readl(gpf3dat) & ~(1 << 5)),gpf3dat); 100 break; 101 #endif 102 } 103 return 0; 104 } 105 106 struct file_operations fs4412_led_ops = { 107 .owner = THIS_MODULE, 108 .open = fs4412_led_open, 109 .unlocked_ioctl = fs4412_led_ioctl, 110 }; 111 112 int __init fs4412_led_init(void) 113 { 114 int i; 115 fs4412_led_major = register_chrdev(0,"led",&fs4412_led_ops); 116 cls = class_create(THIS_MODULE,"led"); 117 118 for(i = 0;i < 4;i ++) 119 devs = device_create(cls,NULL,MKDEV(fs4412_led_major,i),NULL,"led%d",i); 120 121 gpx2con = ioremap(GPX2CON,4); 122 gpx2dat = gpx2con + 1; 123 124 gpx1con = ioremap(GPX1CON,4); 125 gpx1dat = gpx1con + 1; 126 127 gpf3con = ioremap(GPF3CON,4); 128 gpf3dat = gpf3con + 1; 129 130 writel((readl(gpx2con) & ~(0xf << 28)) | 1 << 28,gpx2con); 131 writel((readl(gpx1con) & ~(0xf << 0)) | 1 << 0,gpx1con); 132 writel((readl(gpf3con) & ~(0xff << 16)) | 0x11 << 16,gpf3con); 133 134 return 0; 135 } 136 module_init(fs4412_led_init); 137 138 void __exit fs4412_led_exit(void) 139 { 140 int i; 141 for(i = 3;i >= 0;i --) 142 device_destroy(cls,MKDEV(fs4412_led_major,i)); 143 class_destroy(cls); 144 unregister_chrdev(fs4412_led_major,"led"); 145 return; 146 } 147 module_exit(fs4412_led_exit); 148 MODULE_LICENSE("GPL");
总结:
struct file_operations { ssize_t (*read)(struct file *filp,char __user *ubuf,size_t size,loff_t *off); ssize_t (*write)(struct file *filp,const char __user *ubuf,size_t size,loff_t *off); long (*unlock_ioctl)(struct file *filp,unsigned int cmd,unsigned long arg); }; 读写都是站在用户空间的角度来考虑 输入:数据从内核空间流向用户空间 输出:数据从用户空间流向内核空间 应用层:ssize_t read(int fd,void *buf,size_t size); 驱动层:ssize_t (*read)(struct file *filp,char __user *ubuf,size_t size,loff_t *off); int copy_to_user(void *to,void *from,size_t size); 应用层:ssize_t write(int fd,const void *buf,size_t size); 驱动层:ssize_t (*write)(struct file *filp,const char __user *ubuf,size_t size,loff_t *off); int copy_from_user(void *to,void *from,size_t size); 应用层:int ioctl(int fd,int cmd,...) 驱动层:long (*unlock_ioctl)(struct file *filp,unsigned int cmd,unsigned long arg); 命令分成了4个部分: 2位方向、14位数据类型大小、8位幻数、8位序号 设置命令需要调用一些宏函数: _IO(幻数,序号); _IOR(幻数,序号,数据类型); _IOW(幻数,序号,数据类型); _IOWR(幻数,序号,数据类型); 避免冲突需要查看Documetation/ioctl/ioctl-number.txt: 查看幻数和序号配合使用时哪些值会有冲突。 假设:4个灯,使不同的设备文件(led0 led1 led2 led3)操作不同的led灯。 相应的可以有4个进程,每个进程打开一个设备文件。这四个进程访问的是同一个驱动。 在操作驱动接口时调用的也是相同的接口,这种情况下会造成资源的竞争。 int led_open(struct inode *,struct file *) { num = iminor(inode); filp->private_data = (void *)num; } long led_ioctl() { int num; num = (int)filp->private_data; }