🚀 设备号
✈ 设备号简述
设备号由主设备号和 次设备号构成。主设备号用来表示一个特定的驱动程序。 次设备号用来表示使用该驱动程序的各个设备。 Linux 提供了一个名为dev_t 的数据类型表示设备号, dev_t 定义在文件 include/linux/types.h 里面, 定义如下:
1 2
| typedef __u32 __kernel_dev_t; typedef __kernel_dev_t dev_t
|
由 dev_t 类型的定义可知,设备号是一个32位的变量, 其中 前12 位用来表示主设备号(0-4095 ), 后20 位用来表示次设备号。
Linux 提供了几个宏定义来操作设备号:
1 2 3 4 5
| #define MINORBITS20 #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))
|
✈ 设备号的分配
分配设备号有两种方案,静态分配和动态分配。由于静态分配设备号可能存在冲突问题, 因此建议使用动态分配设备号。
函数定义:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
各参数的含义:
参数 |
含义 |
dev |
保存申请到的设备号。 |
baseminor |
次设备号的起始地址,一般取0,即次设备号从 0 开始。 |
count |
要申请的设备号数量。 |
name |
设备名字。 |
返回值:成功返回 0, 失败返回负数。
动态分配会优先分配 255 到 234 的主设备号。
✈ 设备号的注销
设备号释放函数 :void unregister_chrdev_region(dev_t from, unsigned count);
参数 |
含义 |
from |
要释放的设备号。 |
count |
表示从 from 开始, 要释放的设备号数量。 |
🚀 字符类设备的注册和注销
✈ 初始化cdev 结构体
在 Linux 内核中, 使用 cdev 结构体描述一个字符设备, cdev 结构体的定义如下:
1 2 3 4 5 6 7 8 9
| struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
|
函数 cdev_init 用于初始化 cdev 结构体,其定义如下:
1 2 3
|
void cdev_init(struct cdev *cdev, const struct file_operations *ops);
|
cdev_init 函数只给 cdev 结构体的 ops 成员变量赋了值,一般还需给 owner 赋值:
1
| cdev.owner = THIS_MODULE;
|
✈ 字符类设备的注册和注销
1 2 3 4 5 6 7 8 9 10
|
int cdev_add(struct cdev *cdev, dev_t dev, unsigned count);
void cdev_del(struct cdev *cdev);
|
🚀 创建和卸载设备
✈ 类的创建和删除
内核中 struct class 结构体类型变量对应一个类, 使用宏定义 class_create
创建一个类 ,然后再调用函数 device_create
来在 /dev 目录下创建相应的设备节点。
class_create 的定义:
1 2 3 4 5 6 7
| #define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ })
struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key);
|
class_create 一共有两个参数, 参数 owner 一般为 THIS_MODULE, 参数 name 是类名字。 返回值是个指向结构体 class 的指针, 也就是创建的类。
卸载驱动程序的时候需要删除掉类, 类删除函数为 class_destroy, 函数原型如下:
1
| void class_destroy(struct class *cls);
|
✈ 创建和卸载设备
创建完成一个类后, 使用 device_create 函数在这个类下创建一个设备。 device_create
函数原型如下:
1 2 3 4 5 6 7 8 9 10 11
|
struct device *device_create(struct class *class,struct device *parent,dev_t devt,void *drvdata,const char *fmt, ...);
|
设备删除函数为 device_destroy, 函数原型如下:
1
| void device_destroy(struct class *class, dev_t devt);
|
🚀 字符设备驱动程序框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #include <linux/device.h>
#define DEVICE_NUMBER 1 #define DEVICE_NAME "chrdev" #define DEVICE_CLASS_NAME "chrdev_class" #define DEVICE_NODE_NAME "chrdev_node"
dev_t dev_num; struct cdev cdev; struct device *device; struct class *class;
static int cdev_open(struct inode *inode, struct file *file) { printk("cdev open!!!\n"); return 0; }
static int cdev_close(struct inode *inode, struct file *file) { printk("cdev close!!!\n"); return 0; }
struct file_operations cdev_fops = { .owner = THIS_MODULE, .open = cdev_open, .release = cdev_close, };
static int chrdev_init(void) { int ret; ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME); if (ret < 0) { printk("alloc_chrdev_region error!\n"); return -1; } printk("alloc_chrdev_region succeed!\n"); printk("dev_num = %d\n", dev_num); printk("major_num = %d, minor_num = %d\n", MAJOR(dev_num), MINOR(dev_num)); cdev.owner = THIS_MODULE; cdev_init(&cdev, &cdev_fops); cdev_add(&cdev, dev_num, DEVICE_NUMBER);
class = class_create(THIS_MODULE, DEVICE_CLASS_NAME); device = device_create(class, NULL, dev_num, NULL, DEVICE_NODE_NAME);
return 0; }
static void chrdev_exit(void) { unregister_chrdev_region(dev_num, DEVICE_NUMBER); cdev_del(&cdev); device_destroy(class, dev_num); class_destroy(class); printk("goodbye!\n"); }
module_init(chrdev_init); module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
|