一.设备号
对字符设备的访问是通过文件系统内的设备名称来访问的,设备名称位于目录/dev下.为了便于系统管理,设置了和设备名称一一对应的设备号,它分为主设备
号和次设备号.通常来说,主设备号标示了设备对应的驱动程序,次设备号则用来分辨拥有同一个主设备号的的各个不同设备.
在内核中,设备号使用类型dev_t来保存,它包括了主设备号和次设备号.dev_t是一个32位的整数,其中的12位用来标示主设备号,其余的20位用来标示次设备号.我们可以使用两个宏来获得设备的主设备号及次设备号:
MAJOR(dev_t dev_id);
MINOR(dev_t dev_id);
将主设备号和次设备号转换为dev_t类型,则可以使用下面的宏:
MKDEV(int major, int minor);
其中,major为主设备号,minor为次设备号.
二.分配设备号
在建立一个字符设备之前.首先要申请设备号,完成该功能的函数有两个,都包含在头文件中.下面分别来看这两个文件:
1.int register_chrdev_region(dev_t first, unsigned int count, char *name);
其中, first为要分配的设备编号范围的起始值,经常被置零.count则是所请求的连续设备编号的个数,这意味着只能申请连续的设备编号.
2.int alloc_chrdev_region(dev_t *dev, unsigned firstminor, int count, char *name);
其中dev用于保存申请成功后动态分配的第一个设备号, firstminor则是请求使用的第一个次设备号.其余与上个函数相同.
三.定义并初始化file_operations结构体.
file_operations结构体用于连接设备号和驱动程序的操作.在该结构体的内部包含了一组函数指针,这些函数用来实现系统调用.通常情况下,要注册如下的几个函数:
1.struct module *owner:用来指向拥有该结构体的模块.
2.ssize_t read(struct file *filp, char __user *buf, size_t count, loff_t *f_ops):用来从设备中读取数据.其中:
filp为文件属性结构体指针.
buf为用户态函数使用的字符内存缓冲.
count为要读取的数据数.
f_ops为文件指针的偏移量.
2.ssize_t write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops):用来向设备输入数据.各函数的含义与上个函数相同.
3.int open(struct inode *inode, struct file *):该函数用来打开一个设备文件.
4.int release(struct inode *inode, struct file *):该函数用来关闭一个设备文件.
该结构体的初始化形式如下例:
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.read = read,
.write = write,
.open = open,
.release = release,
}
四.字符设备的注册.
内核内部使用struct cdev结构来表示字符设备.在内核调用设备的操作之前,必须分配或注册一个或者多个该结构体.该结构体包含在头文件中.一般的步骤如下:
首先定义该结构体:
struct cdev my_cdev;
然后即可以初始化该结构,使用如下的函数初始化:
int cdev_init(struct cdev *dev, struct file_operations *fops).
然后定义该结构体中的一个所有者字段:
my_cdev.owner = THIS_MODULE;
最后即可以向模块添加该结构体:
int cdev_add(struct cdev *dev, dev_t dev_num, usigned int count).其中dev是cdev结构体,dev_num是该设备对应的第一个设备编号, count则是与该设备关联的设备编号数量.
五.移除字符设备
void cdev_del(struct cdev *dev);
六.注销设备号.
unregister_chrdev_region(dev_t first, unsigned int count);
以上两个函数一般用于模块出口函数中.
阅读(723) | 评论(0) | 转发(2) |