linux设备驱动开发详解(基于4.0内核)_读书笔记一

时间:2022-07-19 17:55:43

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个参数的类型。