Linux设备驱动开发-file_operations结构体day02

作者: kiki
参考书:
转载请注明出处!
day02
摘要: file_operations()结构体的结构与成员函数

1.file_operations结构体
其成员函数是字符设备驱动与内核虚拟文件系统的接口,是用户空间对Linux进行系统调用最终的落实者,把系统调用和驱动程序关联起来.
注意: __usr是一个宏,其后的指针指向用户空间.
(1)读设备

/*读设备*/
ssize_t xxx_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
  ...
  copy_to_user(buf,...,...);
  ...
}

filp是文件结构体指针
buf是用户空间内存的地址,不宜直接读写
count是要读的字节数
f_pos是读的位置相对于文件开头的偏移

(2)写设备

ssize_t xxx_write(struct file *filp,const char__user *buf,size_t count,loff_t *f_pos)
{
  ...
  copy_from_user(...,buf,...);
  ...
}

filp是文件结构体指针
buf是用户空间内存的地址,不宜直接读写
count是要写的字节数
f_pos是写的位置相对于文件开头的偏移
**说明:**由于用户空间不能直接访问内核空间的内存,所以借助
copy_from_user(),用户空间缓冲区到内核空间的复制
copy_to_user(),内核空间到用户空间缓冲区的复制

当要复制的内存是简单类型时, 如char , int , long , 等,可以用下列函数进行操作:

int val; /*内核空间整型变量*/
...
get_user(val,(int*)arg);   /*用户到内核,arg是用户空间的地址*/
...
put_user(val,(int*)arg);    /*内核到用户,arg时用户空间的地址*/

内核空间在访问用户空间的缓冲区时,需要先检查其合法性,通过access_ok(type,addr,size) 判断,来确定传入的缓冲区的确属于用户空间.如果不做这项检查,操作系统内核很容易被攻击,就会存在安全漏洞.
此函数检查用户空间中的内存块是否可用。如果可用,则返回真(非0值),否则返回假 (0) 。例:

static ssize_t read_port(struct file *file,char __user *buf,size_t count,loff_t *ppos)
{
	unsigned long i = *ppos;
	char __user *tmp = buf;
	
	if (!access_ok(VERIFY_WRITE,buf,count))
		return -EFAULT;
	while (count-- > 0 && i<65536){
		if (__put_user(inb(i),tmp) < 0)
			return -EFAULT;
		i++;
		tmp++;
	}
	*ppos = i;
	return tmp-buf;
}

__put_user()与put_user()的区别:后者会自动进行access_ok()检查.上述代码用到__put_user()是因为我们手动做access_ok()检查.

get_user()和__get_user()区别相似.

copy_from_user()和copy_to_user()内部也进行了access_ok()检查.

(3)I/O控制函数

long xxx_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
	...
	switch (cmd){
	case XXX_CMD1:
		...
		break;
	case XXX_CMD2:
		...
		break;
	default:
		/*不能支持的命令*/
		return -ENOTTY;
	}
	return 0;
}

cmd参数为事先定义的I/O控制命令,arg为对应于该命令的参数.例:
SET_BAUDRATE是设置波特率的命令
arg就应该是波特率值.

2.字符设备驱动中,需要定义一个file_operations实例,并将具体设备驱动函数赋给file_operations的成员:

struct file_operations xxx_fops = {
	.owner = THIS_MODULE,
	.read = xxx_read,
	.write = xxx_write,
	.unlocked_ioctl = xxx_ioctl,
	...
};

以上代码中xxx_fops与day01中模块加载函数的
cdev_init(&xxx_dev.cdev,&xxx_fops)函数进行与cdev的连接.

你可能感兴趣的:(Linux设备驱动开发)