从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中):
01 - 第一个内核模块程序
02 - 注册字符设备驱动
03 - open & close 函数的应用
04 - read & write 函数的应用
05 - ioctl 的应用
06 - ioctl LED灯硬件分析
07 - ioctl 控制LED软件实现(寄存器操作)
08 - ioctl 控制LED软件实现(库函数操作)
09 - 注册字符设备的另一种方法(常用)
10 - 一个cdev实现对多个设备的支持
11 - 四个cdev控制四个LED设备
12 - 虚拟串口驱动
13 - I2C驱动
14 - SPI协议及驱动讲解
15 - SPI Linux驱动代码实现
16 - 非阻塞型I/O
17 - 阻塞型I/O
18 - I/O多路复用之 select
19 - I/O多路复用之 poll
20 - I/O多路复用之 epoll
21 - 异步通知
前面一直使用register_chrdev来注册字符设备,方法简单但是无法指定次设备号,并且会在内核中连续注册了256(分析内核代码中可知),也就是所以的此设备号都会被占用,而在大多数情况下都不会用到这么多次设备号,所以会造成极大的资源浪费。本节采用另一种方法来注册字符设备,该方法可以设置主次设备号及个数但过程比较复杂。
注册字符设备的过程如下
1. 定义字符设备结构体
struct cdev cdev; 或者 struct cdev *cdev;
2 实例化一个字符设备体,为cdev分配内存
struct cdev *cdev_alloc(void);
填充cdev设备体,最主要是将file_operations填充进去
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
注意
cdev_alloc函数针对于需要空间申请的操作,而cdev_init针对于不需要空间申请的操作;
***** 因此如果你定义的是一个指针,那么只需要使用cdev_alloc函数并在其后做一个ops的赋值操作就可以
struct cdev *cdev;
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &fops;
***** 如果你定义的是一个结构体而非指针,那么只需要使用cdev_init函数就可以了。
struct cdev cdev;
cdev_init(struct cdev * cdev, const struct file_operations * fops);
3. 向内核申请设备号
动态申请
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
@功 能: 动态申请设备号
@param1: 分配得到的第一个设备号
@param2: 动态分配的设备号的起始次设备号
@param3: 设备数量
@param4: 标定主设备号的名称
@return: 0表示成功,负数表示失败
@示 例: static dev_t devid;
alloc_chrdev_region(&devid, 0, 1, "tlc5615")
静态申请
int register_chrdev_region(dev_t from, unsigned count, const char *name);
@功能 : 静态注册一个或多个连续的字符设备号
@param1: 指定起始的设备号
@param2: 设备数量
@param3: 标定主设备号的名称
@return: 0表示成功,负数表示失败
注销设备号
void unregister_chrdev_region(dev_t from, unsigned count);
4. 将设备体与设备号绑定,并向内核添加一个字符设备
int cdev_add(struct cdev *cdev, dev_t dev, unsigned count);
@param3: 指定了被添加的cdev可以管理多少个设备
@return: 0表示成功
删除cdev对象:
void cdev_del(struct cdev *)
本节只讲述了注册字符设备的接口函数,下一节将对这几个函数进行实际实现 10-一个cdev实现对多个设备的支持