linux字符设备驱动结构


linux内核中使用cdev结构体来描述字符设备,cdev结构体的定义如下:
struct cdev
{
  struct kobject kobj;       //内嵌的kobject对象
  struct module *owner;  //所属模块
  struct file_operations *ops;  //文件操作结构体
  struct list_head list;  
  dev_t dev;                    //设备号
  unsigned int count;
};

cdev结构体的dev_t成员定义了设备号,为32位,其中高12位为主设备号,低20位为此设备号。file_operations则定义了字符设备驱动提供给虚拟文件系统的接口函数。
linux内核提供了一组函数用于操作cdev结构体:
void cdev_init(struct cdev *,struct file_operations * );
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *,unsigned dev_t);
void cdev_del(struct cdev * );

cdev_init()函数用于初始化cdev的成员,并建立cdev和file_operation之间的连接。
cdev_alloc函数用于动态申请一个cdev内存。
cdev_add()函数和cdev_del()函数分别向系统添加和删除一个cdev,完成字符设备的注册和注销。对cdev_add()的调用通常发生在字符设备驱动模块加载函数中,而对cdev_del()函数的调用则通常发生在字符设备驱动模块卸载函数中。

在调用cdev_add()函数向系统注册字符设备之前,应首先调用
int register_chrdev_region(dev_t from,unsigned count,const char * name);
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char * name);
前者用于已知起始设备的设备号的情况,后者用于设备号未知,向系统动态申请未被占用的设备用的情况。
释放设备号用:
void unregister_chrdev_region(dev_t from, unsigned count);

file_operation结构体的成员函数是字符设备驱动程序设计的主体内容。


linux字符设备驱动的组成

1.字符设备驱动模块加载与卸载函数(以rtc为例)
module_init(s3c_rtc_init);
static int __init s3c_rtc_init(void)
{
	printk(banner);
	return platform_driver_register(&s3c2410_rtc_driver);
}
module_exit(s3c_rtc_exit);
static void __exit s3c_rtc_exit(void)
{
	platform_driver_unregister(&s3c2410_rtc_driver);
}
2.字符设备驱动的file_operation结构体中成员函数
1  struct file_operations 
2  { 
3    struct module *owner; 
4      // 拥有该结构的模块的指针,一般为THIS_MODULES 
5    loff_t(*llseek)(struct file *, loff_t, int); 
6      // 用来修改文件当前的读写位置  
7    ssize_t(*read)(struct file *, char _  _user *, size_t, loff_t*); 
8      // 从设备中同步读取数据 
9    ssize_t(*aio_read)(struct  kiocb  *,  char  _  _user  *,  size_t,  loff_t); 
10     // 初始化一个异步的读取操作 
11   ssize_t(*write)(struct  file  *,  const  char  _  _user  *,  size_t, 
loff_t*); 
12     // 向设备发送数据 
13   ssize_t(*aio_write)(struct kiocb *, const char _  _user *, size_t, 
loff_t); 
14     // 初始化一个异步的写入操作 
15   int(*readdir)(struct file *, void *, filldir_t); 
16     // 仅用于读取目录,对于设备文件,该字段为 NULL 
17   unsigned int(*poll)(struct file *, struct poll_table_struct*); 
18     // 轮询函数,判断目前是否可以进行非阻塞的读取或写入 
19   int(*ioctl)(struct  inode  *, struct  file *, unsigned  int, unsigned 
long); 
20     // 执行设备I/O控制命令 
21   long(*unlocked_ioctl)(struct  file  *,  unsigned  int,  unsigned  long); 
22     // 不使用BLK文件系统,将使用此种函数指针代替ioctl 
23   long(*compat_ioctl)(struct file *, unsigned int, unsigned long); 
24     // 在64位系统上,32位的ioctl调用将使用此函数指针代替 
25   int(*mmap)(struct file *, struct vm_area_struct*); 
26     // 用于请求将设备内存映射到进程地址空间 
27   int(*open)(struct inode *, struct file*); 
28     // 打开 
29   int(*flush)(struct file*); 
30   int(*release)(struct inode *, struct file*); 
31     // 关闭 
32   int(*synch)(struct file *, struct dentry *, int datasync); 
33     // 刷新待处理的数据 
34   int(*aio_fsync)(struct kiocb *, int datasync); 
35     // 异步fsync 
36   int(*fasync)(int, struct file *, int); 
37     // 通知设备FASYNC标志发生变化 
38   int(*lock)(struct file *, int, struct file_lock*); 
39   ssize_t(*readv)(struct  file  *,  const  struct  iovec  *,  unsigned  long, 
loff_t*); 
40   ssize_t(*writev)(struct  file  *,  const  struct  iovec  *,  unsigned  long,
loff_t*); 
41     // readv和writev:分散/聚集型的读写操作 
42   ssize_t(*sendfile)(struct  file  *,  loff_t  *,  size_t,  read_actor_t, 
void*); 
43     // 通常为NULL 
44   ssize_t(*sendpage)(struct file *, struct page *, int, size_t, 
loff_t *, int); 
45     // 通常为NULL 
46   unsigned long(*get_unmapped_area)(struct file *,unsigned long, 
unsigned long, 
47     unsigned long, unsigned long); 
48     // 在进程地址空间找到一个将底层设备中的内存段映射的位置 
49   int(*check_flags)(int); 
50     // 允许模块检查传递给fcntl(F_SETEL...)调用的标志 
51   int(*dir_notify)(struct file *filp, unsigned long arg); 
52     // 仅对文件系统有效,驱动程序不必实现 
53   int(*flock)(struct file *, int, struct file_lock*);  
54 }; 

字符设备驱动的结构如下:

设备驱动程序编写流程
设备驱动程序可以使用模块的方式加载的方式加载到内核中去,驱动开发时时没有main()函数,模块在调用insmod命令时被加载,此时的入口点式init_module()函数,通常在该函数中完成设备的注册。同样,模块在调用rmmod命令时被卸载,此时的入口点是cleanup_module()函数,在该函数中完成设备的卸载。在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,如open()、read()、write()等,而驱动程序就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作。

设备功能是由file_operation()函数定义的。

proc文件系统
/proc文件系统是一个伪文件系统,它是一种内核和内核模块用来向进程发送信息的机制。这个伪文件系统让用户可以和内核内部数据结构进行交互,获取有关系统和进程的有用信息,在运行时通过改变内核参数来改变设置。与其他文件系统不同,/proc存在于内存之中而不是在硬盘之中。/proc文件系统体现了内核及进程运行的内容,在加载模块成功后,读者可以通过查看/proc/device文件获得相关设备的主设备号


通常情况下,字符驱动程序主要还是根据linux中已有的模板进行更改。



















Logo

更多推荐