简介
linux驱动层次图:
用户程序和内核驱动、硬件数据交互方法:
1、利用/dev设备节点去读、写和ioctl操作
2、利用/proc和/sys伪文件系统
驱动程序分类:
1、字符设备驱动:按字节流访问操作,如鼠标、键盘
2、块设备驱动:按block访问操作,带有缓冲,如磁盘、U盘
3、网络设备驱动:如网卡
字符设备驱动操作
1、设备号:
设备编号是设备在linux下的标识ID,这个ID在系统中唯一
设备号 由 主设备号 和 此设备号组成
主设备号:表示一类设备,表示一个特定的设备驱动程序
次设备号:表示一个特定的设备
2、数据结构:
linux内核中,dev_t类型用来表示设备号,是一个无符号32位整形数据,
3、设备号的操作宏:
MKDEV(major, minor); //生成一个dev_t的设备号,major是主设备号,高24位,minor是次设备号,低8位
MAJOR(devno); //从设备号devno中获取主设备号
MINOR(devno); //从设备号devno中获取次设备号
4、设备号申请:
静态申请:用于驱动开发者事先知道该驱动主设备号
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number. 申请设备的第一个设备的设备编号
* @count: the number of consecutive device numbers required 申请多少个设备编号
* @name: the name of the device or driver. 设备的名字
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region (dev_t from, unsigned count, const char *name);
动态申请:这种方式由系统动态分配设备号
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number 返回第一个设备编号的地址
* @baseminor: first of the requested range of minor numbers 第一个设备编号的次设备号
* @count: the number of minor numbers required 申请多少个设备编号
* @name: the name of the associated device or driver 设备的名字
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
cd = __register_chrdev_region(0, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
*dev = MKDEV(cd->major, cd->baseminor);
return 0;
}
释放设备编号:
/**
* unregister_chrdev_region() - unregister a range of device numbers
* @from: the first in the range of numbers to unregister 要释放设备的第一个设备的设备编号
* @count: the number of device numbers to unregister 释放多少个设备编号
*
* This function will unregister a range of @count device numbers,
* starting with @from. The caller should normally be the one who
* allocated those numbers in the first place...
*/
void unregister_chrdev_region(dev_t from, unsigned count)
5、注册初始化字符设备
linux内核中对每一类设备都有相应的数据结构表示,字符设备使用struct cdev(linux/cdev.h)来表示,就会字符设备驱动必须申请struct cdev实例来表示
struct cdev {
struct kobject kobj; //kobject设备对象
struct module *owner; //数据哪个模块
const struct file_operations *ops; //文件操作
struct list_head list; //链表结构
dev_t dev; //设备号
unsigned int count; //字符设备的数量
} __randomize_layout;
cdev字符设备实例操作函数:
1、给字符设备申请空间
struct cdev *cdev_alloc (void);
2、初始化字符设备
void cdev_init (struct cdev *dev, const struct file_operation *fops);
dev:字符设备结构实例地址
fops:文件操作结构实例地址
3、注册字符设备
int cdev_add (struct cdev *dev, dev_t devno, unsigned count);
dev:字符设备结构实例地址
devno:设备编号
count:设备数量
6、创建设备节点
1、mknod:手动创建
mknod /dev/name c major minor
其中 “name”可以是任意符合unix下路径名的名字,不一定要是代码里定义的驱动或设备的名字;c 表示创建字符设备节点, major是成功申请的主设备号,minor是次设备号,这个可以是任意的(在次设备号范围内)
2、udev自动创建
需要在代码中创建一个设备类,然后在这个设备类的基础上,创建一个设备号,
定义全局变量:
struct class *gclass = NULL;
struct device *gdevice = NULL;
gclass = class_create (THIS_MODULE, "jiedian"); //创建一个设备类
gdevice = device_create (gclass,
NULL, //父设备 parent
devno, //设备编号
NULL, //设备私有数据
"jiedian"); //设备节点的名字
删除设备节点:
device_destroy (gdevice, devno); //移除设备 自动删除已经创建的节点
class_destroy (gclass); //移除设备类
总结:
Step 1:申请设备号(静态、动态)
Step 2:注册字符设备 (围绕struct cdev)
struct cdev *cdev_alloc(void);
void cdev_init(struct cdev *cdev, const struct file_operations *fops) ;
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
Step 3:创建设备节点 (两种方式)
通过 mknod命令手动创建;
通过udev自动生成(后台跑udevd应用程序)