Linux设备驱动开发 - 字符设备驱动

时间:2022-12-17 17:54:27

struct cdev结构体:

1 struct cdev {
2     struct kobject kobj; /* 内嵌的kobject对象 */
3     struct module *owner; /* 所属模块 */
4     const struct file_operations *ops; /* 文件操作结构体 */
5     struct list_head list;
6     dev_t dev; /* 设备号 */
7     unsigned int count;
8 };

struct file_operations原形:

 1 struct file_operations {
 2 struct module *owner;
 3 loff_t (*llseek) (struct file *, loff_t, int);
 4 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 5 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 6 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 7 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 8 int (*readdir) (struct file *, void *, filldir_t);
 9 unsigned int (*poll) (struct file *, struct poll_table_struct *);
10 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
11 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
12 int (*mmap) (struct file *, struct vm_area_struct *);
13 int (*open) (struct inode *, struct file *);
14 int (*flush) (struct file *, fl_owner_t id);
15 int (*release) (struct inode *, struct file *);
16 int (*fsync) (struct file *, int datasync);
17 int (*aio_fsync) (struct kiocb *, int datasync);
18 int (*fasync) (int, struct file *, int);
19 int (*lock) (struct file *, int, struct file_lock *);
20 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
21 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
22 int (*check_flags)(int);
23 int (*flock) (struct file *, int, struct file_lock *);
24 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
25 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
26 int (*setlease)(struct file *, long, struct file_lock **);
27 long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
28 };

file_operations里面的函数会在用户空间执行文件操作时被调用。
read()函数在用户空间执行设备文件读时调用,write()函数在用户空间执行设备文件写时调用,open()函数在用户空间执行设备文件打开时调用。
release()函数在用户空间执行close()时调用。
unlocked_ioctl()在用户空间与之对应的函数为ioctl()函数。
poll()函数用于轮询操作,用户空间与之对应的函数为select()函数。

在设备驱动模块加载函数中要完成的主要工作是对字符设备进行初始化和注册。


字符设备初始化函数:

1 void cdev_init(struct cdev *cdev, const struct file_operations *fops);

 

字符设备注册函数:

1 int register_chrdev_region(dev_t from, unsigned count, const char *name);
2 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

register_chrdev_region()函数用于已知设备号的情况,from为要注册的设备号,count为需要注册的设备的个数,name为设备名。
alloc_chrdev_region()函数注册可以不提供设备号,系统自动分配一个设备号并保存在参数dev中,baseminor为注册设备的起始设备号,count和name参数跟register_chrdev_region()函数一样。

注册完成后需调用cdev_add()函数将字符设备添加到系统中。

1 int cdev_add(struct cdev *p, dev_t dev, unsigned count);

参数p为cdev结构体,dev为设备驱动号,count表示要添加的个数。

在设备驱动模块卸载函数中需对字符设备进行注销并释放设备号。


注销字符设备:

1 void cdev_del(struct cdev *p);

需要传进来的参数为字符设备cdev结构体。

 

释放设备号:

1 void unregister_chrdev_region(dev_t from, unsigned count);

from为要释放的字符设备号,count为要释放的个数。

用户空间和内核进行数据的传递需要借助两个函数:

1 copy_to_user(to ,from ,n); //给用户空间拷贝数据
2 copy_from_user(to ,from ,n); //拷贝用户空间的数据到内核

从from拷贝n个字节到to


字符设备驱动模板:

 1 struct cdev xxxx_cdev;
 2 dev_t xxx_devno;
 3 
 4 static int xxx_open(struct inode *node, struct file *filep)
 5 {
 6     int ret = 0;
 7     ...
 8     
 9     return ret;
10 }
11 
12 static ssize_t xxx_read(struct file *filep, char __user *buffer, size_t offset, loff_t *ppos)
13 {
14     ...
15     copy_to_user(buffer,...,...);    //给用户空间拷贝数据
16     ...
17 }
18 
19 static ssize_t xxx_write(struct file *filep, char __user *buffer, size_t offset, loff_t *ppos)
20 {
21     ...
22     copy_from_user(...,buffer,...);  //拷贝用户空间的数据到内核
23     ...
24 }
25 
26 static struct file_operations xxx_fops = 
27 {
28     .open = xxx_open,
29     .write = xxx_write,
30     .read = xxx_read,
31     ...
32 }
33 
34 static int __init xxx_init(void)
35 {
36     ...
37     /* cdev初始化 */
38     cdev_init(&xxx_cdev, &xxx_fops);
39     
40     /* 注册字符设备驱动 */
41     alloc_chrdev_region(&xxx_devno,0,1,DEV_NAME);
42     cdev_add(&xxx_cdev,xxx_devno,1);
43     ...
44 }
45 
46 static void __exit xxx_exit(void)
47 {
48     ...
49     /* 注销设备驱动 */
50     cdev_del(&xxx_cdev);
51     
52     /* 释放设备号 */
53     unregister_chrdev_region(xxx_devno,1);
54     ...
55 }

 

 

 Linux设备驱动开发 - 字符设备驱动