Linux设备驱动程序设计(一)

Linux驱动程序概述

Linux设备驱动程序是为特定的硬件提供给应用程序的一组标准化接口,它隐藏了设备工作的细节。用户程序通过标准化系统调用,这些调用和特定的硬件是无关的,再由Linux内核调用特定的设备驱动程序操作和控制特定的实际的硬件设备。其中,Linux系统设备分为三种类型:

  • 字符设备(character device)
  • 块设备(block device)
  • 网络接口(network interface)

本篇文章中,主要谈论字符设备驱动程序的设计。
字符设备是能够像字节流一样被访问的设备,一般不使用缓存技术。字符设备驱动程序最少应实现open、close、read和write系统调用。典型的字符设备例子是终端设备(/dev/console)和串口(/dev/ttyS0)。

字符驱动程序的调用过程

用户调用设备驱动函数
如上图,用户想要调用Linux设备驱动函数对设备进行操作时,由Linux系统调用来间接调用设备驱动函数,此时系统由用户态陷入内核态,完成对设备的操作后(即完成内核态数据和用户态数据的交换),操作系统又返回用户态。

驱动程序的一些基本知识

主设备号和次设备号

Linux系统为每一个设备分配了一个主设备号和次设备号,主设备号标识一类设备对应的驱动程序,次设备号用来标识一类设备中的某个具体设备。比如说:现在有打印机这个设备,主设备号就只用来标识打印机这类设备,区别于其他如鼠标,传真机等设备;而次设备号是用来区别不同打印机,如打印机A和打印机B,它俩主设备号一样,但次设备号不同。

设备文件的操作

Linux系统访问设备就像访问文件一样,例如打开设备使用系统调用open(),关闭设备使用系统调用close()。读写设备使用系统调用read()和write()。在Linux内核中,字符设备使用struct file_operations结构来定义设备的各种操作集合,结构中的各个函数分别响应同名或类似的名称的系统调用。struct file_operations结构在Linux系统下的定义如下:

struct file_operations
	{
		struct module *owner;
		loff_t (*llseek) (struct file *, loff_t, int);
		ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
		ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
		ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
		ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
		int (*readdir) (struct file *, void *, filldir_t);
		unsigned int (*poll) (struct file *, struct poll_table_struct *);
		int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
		long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
		long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
		int (*mmap) (struct file *, struct vm_area_struct *);
		int (*open) (struct inode *, struct file *);
		int (*flush) (struct file *);
		int (*release) (struct inode *, struct file *);
		int (*fsync) (struct file *, struct dentry *, int datasync);
		int (*aio_fsync) (struct kiocb *, int datasync);
		int (*fasync) (int, struct file *, int);
		int (*lock) (struct file *, int, struct file_lock *);
		ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t*);
		ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t*);
		ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void*);
		ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
		unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
		int (*check_flags)(int);
		int (*dir_notify)(struct file *filp, unsigned long arg);
		int (*flock) (struct file *, int, struct file_lock *);
};

主要函数的作用

ioctl函数
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

ioctl 函数提供用户程序对设备执行特定的命令的方法,比如设置设备驱动程序内部参数,控制设备操作特性,或其他不是读也不是写的操作等。调用成功返回非负值。

open函数
int (*open) (struct inode *, struct file *);

open 函数用来打开设备。如果该函数没有实现,系统调用 open 总是成功,但驱动程序得不到任何打开设备的通知。

read/write函数
ssize_t (*read) (struct file * filp, char __user * buffer, size_t size, loff_t * opps);
ssize_t (*write) (struct file * filp, const char __user * buffer, size_t size, loff_t * opps);

(指针参数 filp 为进行读取(写)信息的目标文件,指针参数buffer 为对应放置信息的缓冲区(即用户空间内存地址),参数size为要读取的信息长度,参数 opps 为读(写)的位置相对于文件开头的偏移,在读取信息后,这个指针一般都会移动,移动的值为要读取信息的长度值)。
如果返回值为非负,则操作成功。

llseek函数
loff_t (*llseek) (struct file * filp , loff_t p, int orig);

指针参数filp为进行读取信息的目标文件结构体指针;参数 p 为文件定位的目标偏移量;参数orig为对文件定位的起始地址,其中,orig=0,则表示从文件头开始;orig=1,则表示从当前位置开始;origin=2,表示从文件末尾开始。

llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值.

loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽。

编写字符设备驱动程序,主要是实现struct file_operations结构中的各个函数。当然,驱动程序并不是要实现所有的这些函数,可以根据实际设备需要实现必要的函数即可。

模块注册和卸载的函数调用

在 Linux 系统中注册设备时,通常需要传递三个重要参数:主设备号设备名称设备文件结构
主设备号是在系统内是唯一标识设备类型的定义,向内核申请设备注册或卸载时,都需要传递主设备号。主设备号的定义有两种方法:

  1. 指定一常数作为主设备号,只要不与已有设备号冲突,即可把该设备号唯一地指派给该设备;
  2. 以设备号0进行注册申请,由系统返回一个可用设备号作为主设备号。

设备注册或卸载时还包括传递一个设备名称,用户程序使用这个名称来打开设备。
设备是以文件的形式存在的,所以设备注册时需要使用一个文件结构struct file_operations定义,内核使用此结构定位驱动程序的操作函数。
字符设备的注册通过调用register_chrdev来注册,通过unregister_chrdev_region来卸载:

/* 字符设备注册函数 */
int register_chrdev (unsigned int major, const char *name, struct file_operations *fops);
/*字符设备卸载函数*/
static inline void unregister_chrdev(unsigned int major, const char *name);
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值