2012年(10)
分类: LINUX
2012-06-01 17:44:42
字符设备通过字符设备文件来存取。字符设备文件由使用ls –l的输出的第一列“c”标识。如果使用ls –l 命令,会看到在设备文件项中有2个数(由一个逗号分隔)这些数字就是设备文件的主次设备编号。
前面一个数字4是主设备号,它是用来标识与设备文件相连的驱动程序。后面的92、93、67等数字是次设备号,它是被驱动程序用来辨别操作的是同类型的哪个设备。
设备号在内核中是用dev_t来描述的:高12位为主设备号,低20位是次设备号
提取主设备号:MAJOR(dev_t dev)
提取次设备号:MINOR(dev_t dev)
LINUX下设备号的分配:静态申请,动态申请两种方法
静态申请:查看Documentation/devices.txt,确定一个没有使用过的主设备号,使用register_chrdev_region函数注册设备号(缺点:如果没有分析好,或者驱动程序使用广泛的时候,可能会发生设备号冲突,使驱动程序无法注册)
Int register_chrdev_regio(dev_t from,unsigned count,const char *name):注册从from开始的count个设备号(主设备号不变,次设备号增加,如果次设备号溢出,主设备号加1)
参数:from:要注册的第一个设备号 count:要注册的设备号个数 name:设备名(体现在/proc/devices)
动态申请:使用alloc_chrdev_region分配设备号(缺点:无法在安装驱动前创建设备文件。解决办法:安装驱动后,从/proc/devices中查询设备号)
Int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name):动态申请count个设备号,第1个设备号的次设备号为baseminor。
参数:dev:分配到的设备号(不需要传值,用来获取主设备号)baseminor:起始次设备号 count:要注册的设备号个数 name:设备名(体现在/prroc/devices)
在不再使用设备号时,应释放这些设备号:void unregister_chrdev_region(dev_t from, unsigned count)释放从from开始的count个设备号。
创建设备文件:mknod命令手工创建,自动创建
Mknod手工创建:mknod filename type major minor
Filename:设备文件名 type:设备文件类型 major:主设备号 minor:次设备号
自动创建:安装成功驱动后,自动创建文件。
利用udev(mdev)来实现设备文件的自动创建很简单,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用device_create创建对应的设备.
例子:
Struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
Device_create(myclass, NULL, MKDEV(major_num,0), NULL, “my_device”);
当驱动被加载时,udev(mdev)就会自动在/dev下创建my_device设备文件。
重要数据结构:
struct file:代表一个打开的文件。系统中每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,在文件关闭后释放。重要成员:loff_t f_pos/*文件读写位置*/ struct file_operations *f_op
struct inode:用来记录文件的物理上的信息。因此,它和代表打开文件的file结构不同。一个文件可以对应多个file结构,但只有一个inode结构。重要成员:dev_t i_rdev:设备号。
:一个函数指针的集合,定义能在设备上进行的操作。结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL。
设备注册:字符设备使用struct cdev来描述,注册分为三步:分配cdev,初始化cdev,添加cdev。
分配cdev: struct cdev * cdev_alloc(void);
初始化cdev:void cdev_init(struct cdev *cdev,const struct file_operations *fops)参数cdev:待初始化的cdev结构;fops:设备对应的操作函数集
添加(注册)cdev:ini cdev_add(struct cdev *p,dev_t dev,unsigned count)参数:p待添加到内核的字符设备结构;dev:设备号;count:添加的设备个数
设备操作:
Int (*open)(struct inode *,struct file *)
在设备文件上的第一个操作,并不要求驱动程序一定要实现这个方法。如果该项为NULL,设备的打开操作永远成功。
Void (*release)(struct inode *,struct file *)
当设备文件被关闭时调用这个操作。与open相仿,release也可以没有。
Ssize_t (*read)(struct file *filp,char __user *buff,size_t count,loff_t *offp)
从设备中读取数据
Ssize_t (*write)(struct file *,const char __user *,size_t,loff_t *)
向设备发送数据
Unsigned int (*poll)(struct file *,struct poll_table_struct *)
对应select系统调用
Int (*ioctl)(struct inode *,struct file *,unsigned int,unsigned long)
控制设备
Int (*mmap)(struct file *,struct vm-area_struct *)
将设备映射到进程虚拟地址空间中
Off_t (*llseek) (struct file *,loff_t,int)
修改文件的当前读写位置,并将新位置作为返回值。
Open方法:open方法是驱动程序用来为以后的操作完成初始化准备工作的。在大部分驱动程序中,open完成初始化设备和标明次设备号的工作。
Release方法:与open相反,这个方法有时也称为close,它应该完成关闭设备的作用。
读和写:
//filp是文件指针,count是请求传输的数据量,buff参数指向数据缓存,offp指出文件当前的访问位置。Buff参数是用户空间指针,它在内核空间时可能根本是无效的——没有那个地址的映射,它不能被内核代码直接引用。
内核提供了专门的函数用于访问用户空间的指针,如:
Int copy_from_user(void *to,const void __user *from,int n)
Int copy_to_user(void __user *to,const void *rom,int n)
设备注销:
Int cdev_del(struct cdev *p)参数P:要注销的字符设备结构