驱动开发之read/write

驱动开发之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 }
app.c 
#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);
demo.c

分支匹配函数:

应用层:

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)
head.h
 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 }
app.c
 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);
demo.c

2.led:

head.h
 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 }
app.c
 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");
led.c

3.资源的竞争

1 #define LED_ON         _IO('x',0)
2 #define LED_OFF     _IO('x',1)
head.h
 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 }
app0.c
 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 }
app1.c
 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 }
app2.c
 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 }
app3.c
  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");
led_muiltfile.c

  

总结:

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;
}
课程总结


原文地址:https://www.cnblogs.com/hslixiqian/p/9655949.html