0. 字符设备驱动简介
字符设备驱动是Linux驱动中最基本的一类设备驱动,也是我们学习重点。
我们从下面这一张图来详解字符设备驱动
在linux内核中,使用cdev结构体来描述一个字符设备驱动,关于cdev的结构体的定义和操作在include\linux\cdev.h
头文件中
#ifndef _LINUX_CDEV_H
#define _LINUX_CDEV_H
#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>
struct file_operations;
struct inode;
struct module;
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);
void cd_forget(struct inode *);
#endif
1. cdve 结构体
struct cdev {
struct kobject kobj; /*内嵌的kobject对象方便以后引用,也会在sys下生成相关的设备文件*/
struct module *owner; /*所属于的模块,正常的就是本模块THIS_MODULE*/
const struct file_operations *ops;/*linux下一切皆文件,字符设备也是文件。*/
struct list_head list;/*字符设备的链表头*/
dev_t dev; /*设备号*/
unsigned int count;
};
在字符设备中最重要的就是 const struct file_operations *ops
dev_t dev
2个成员。
2.dev_t Linux设备号
dev_t 类型定义在include/linux/types.h
头文件中。
typedef __u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
Linux设备号位32位,其高12位为主设备号,第20为次设备号。include\linux\kdev_t.h
头文件中定义了设备号相关宏和函数。
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
2.1设备号的分配
-
静态分配设备号(自己确定设备的设备号)
内核如何管理设备驱动:内核中有一个255个元素的数组来存储字符设备驱动。
有一些常用的设备号已经给Linux内核开发使用了,具体如miscdev input。分配的内容可以查看文档 Documentation/devices.txt。
查看当前系统中所使用的设备号:
cat /proc/devices
-
动态分配设备号(由系统分配)
静态分配设备号需要我们检查当前系统中所有被使用了的设备号,然后挑选一个没有使用的。而且静态分配设备号很容易带来冲突问题, Linux 社区推荐使用动态分配设备号,在注册字符设备之前先申请一个设备号,系统会自动给你一个没有被使用的设备号,这样就避免了冲突。卸载驱动的时候释放掉这个设备号即可。
设备号的申请函数:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
设备号的注册函数:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
设备号的释放函数:
void unregister_chrdev_region(dev_t from, unsigned count)
3. cdev操作函数
void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);
void cd_forget(struct inode *);
cdev_add()函数和cdev_del()函数分别向系统添加和删除一个cdev,完成字符设备的注册和注销。对cdev_add()的调用通常发生在字符设备驱动的模块加载函数中,而对cdev_del()函数通常发生在字符设备驱动模块卸载函数中。