linux设备驱动开发详解(基于4.0内核)_读书笔记一
ssize_t xxx_read(struct file *filp, char __user *buf, size_tcount, loff_t *f_ops);
filp是文件结构体指针,buf是用户空间内存的地址,该地址在内存空间不宜直接读写,count是要读的字节数,f_ops是读的位置相对于文件开头的偏移。
ssize_t xxx_write(struct file *filp, char __user *buf, size_tcount, loff_t *f_ops);
同上
long xxx_ioctl(struct file *filp, unsigned int cmd, unsignedlong arg);
cmd参数为实现定义的I/O控制命令,而arg为对应于该命令的参数。例如,若SET_BAUDRATE是设置波特率的命令,那后面的arg就应该是波特率的值。
由于用户空间不能直接访问内核空间的内存,因此借助了2个函数(函数内部已经检查过缓冲区的合法性):
unsigned long copy_from_user(void *to, const void __user*from, unsigned long count);
unsigned long copy_to_user(void __user *to, const void *from,unsigned long count);
函数均返回不能被复制的字节数,复制成功返回0。复制失败返回负值。
如果要负值的内存是简单类型,如char、int、long等,则可以使用简单的(函数内部已经检查过缓冲区的合法性):
put_user(val. (int *) arg);
get_user(val, (int *) arg);
内核访问用户空间的缓冲区时,一般需要先检查其合法性,通过access_ok(type, addr,size)进行判断,以确定传入的缓冲区的确属于用户空间。检查后,可使用__put_user()代替put_user()。
cdev_add(struct cdev *dev, dev_t num, unsigned int count);
dev是cdev结构体,num是该设备对应的第一个设备编号,count是应该和该设备关联的设备编号的数量。
alloc_chrdev_region(dev_t*dev, unsigned int firstminor, unsigned int count, const char *name);
该函数需要传递给它指定的第一个次设备号firstminor(一般为0)和要分配的设备数count,以及设备名,调用该函数后自动分配得到的设备号保存在dev中。
Linux建议的io控制命令组成。
设备类型 |
序列号 |
方向 |
数据尺寸 |
8位 |
8位 |
2位 |
13/14位 |
内核定义了以下4个宏来辅助生成命令。
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
type(设备类型字段)、nr(序列号字段)、size(数据长度字段)、宏名隐含的方向字段。
例如:#define GLOBALMEM_MAGIC‘g’
#define MEM_CLEAR_IO(GLOBALMEM_MAGIC, 0)
预定义的命令(include/uapi/asm-generic/ioctl.h)
FIOCLEX:即File IOctl Close on Exec,对文件设置专用标志,通知内核当exec()系统调用发生时自动关闭打开的文件。
FIONCLEX:即File IOctl Not CLose on Exec,与FIOCLEX标志相反,清除由FIOCLEX命令设置的标志。
FIOQSIZE:获得一个文件或者目录的大小,当用于设备文件时,返回一个 ENOTTY 错误。
FIONBIO:即File IOctl Non-Blocking I/O,这个调用修改在 filp->f_flags 中的 O_NONBLOCK 标志。
使用文件私有数据
struct globalmem_dev *dev = filp->private_data;
实际上,大多数Linux驱动遵循一个“潜规则”,将文件的私有数据private_data(file结构体里面的定义:void *private_date; 注释/*needed for ttydriver, and maybe others*/)
向设备结构体,再用read()、write()、ioctl()、llseek()等函数通过private_data访问设备结构体。
个人理解:案例中用来存私有的数据,用这设备指针指向用户申请的内存区域。体现面向对象的设计思想。
私有数据的设置是在globalmem_open()中完成。
filp->private_data = globalmem_devp; //将设备结构体指针赋值给文件私有数据指针。
container_of()的作用是通过结构体成员的指针找到对应结构体指针。
例:container_of(inode->i_cdev, struct globalmem_dev, cdev);
第1个参数是结构体成员的指针,第2个参数为整个结构体的类型,第3个参数为传入第1个参数的类型。