6.4 Linux 驱动开发 字符设备驱动模型
- 字符(char)设备是个能够像字节流(类似文件)一样被访问的设备。
- 对字符设备发出读/写请求时,实际的硬件I/O操作一般紧接着发生。
- 字符设备驱动程序通常至少要实现 open、close、read 和 write 系统调用。
- 比如我们常见的 lcd、触摸屏、键盘、led、串口等等,他们一般对应具体的硬件都是进行数据的采集、处理、传输。
一、在 Linux 中使用 cdev 结构体表示一个字符设备
struct cdev {
struct kobject kobj;
struct module *owner;//THIS_MODULE
const struct file_operations *ops;//设备操作函数集合
struct list_head list;
dev_t dev;//设备号,高12主,低12次。分解主设备号:MAJOR(dev_t dev);分解次设备号:MINOR(dev_t dev)。合成设备号:MKDEV(int major,int minor)。
unsigned int count;//设备数目
};
- __init:使用 __init 宏标记的函数,在内核完成初始化操作后,会将该函数所在的内存区域释放,从而节省内核的内存空间。
- __exit:__exit 宏会将标记的函数放置在特殊的 .exit.text 段中。如果内核配置支持模块卸载,那么在卸载模块时,会从 .exit.text 段中取出退出函数并执行。如果内核配置不允许模块卸载,.exit.text 段中的代码将不会被链接到最终的内核镜像中,从而节省空间。
二、设备号的申请与释放
//静态申请 newchr.devid = MKDEV(newchr.major , 0);//合成设备号 register_chrdev_region(newchr.devid, NEWCHR_CNT, NEWCHR_NAME);//必须显示人为指定设备号,向内核申请 //动态申请 alloc_chrdev_region(&newchr.devid , 0 , NEWCHR_CNT , NEWCHR_NAME); //由操作系统内核进行分配并申请。 newchr.major = MAJOR(newchr.devid); newchr.minor = MINOR(newchr.devid); //设备号的释放 unregister_chrdev_region(newchr.devid , NEWCHR_CNT);
三、字符设备的初始化与注册
static struct file_operations chrdevbase_fops = {
.owner = THIS_MODULE,
.open = chrdevbase_open,
.read = chrdevbase_read,
.write = chrdevbase_write,
.release = chrdevbase_release,
};
void cdev_init(struct cdev *cdev, const struct file_operations *fops); //初始化 cdev 结构体
int cdev_add(struct cdev *cdev, dev_t dev, unsigned count); // 将设备添加到内核:cdev结构体,要绑定的起始设备号,设备个数
void cdev_del(struct cdev *cdev); // 从 Linux 内核中注销相应的字符设备
四、设备操作函数
int (*open)(struct inode *, struct file *) //打开设备,响应open系统调用 int (*release)(struct inode *, struct file *);//关闭设备,响应close系统调用 loff_t (*llseek)(struct file *, loff_t, int) //重定位读写指针,响应lseek系统调用 ssize_t (*read)(struct file *, char __user *, size_t, loff_t *) //从设备读取数据,响应read系统调用 ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *) //向设备写入数据,响应write系统调用
五、完整示例
#define NEWCHR_CNT 1
#define NEWCHR_NAME "NEWCHR"
//内核缓存区
static char readbuf[100]; //读数据缓存
static char writebuf[100]; //写数据缓存
static char kerneldata[] = {"kernel data!"}; //测试数据
//硬件寄存器
#define GPIO_TEST_BASE (0x01234567) //宏定义寄存器映射地址
static void __iomem *GPIO_TEST; // __iomem 类型的指针,指向映射后的虚拟空间首地址
/* newchr设备结构体 */
struct
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++/嵌入式开发 秋招面经 文章被收录于专栏
一名985硕,在25年秋招中斩获多个C++/嵌入式开发Offer。本专栏将分享我的面经,涵盖C/C++、操作系统、计算机网络、ARM体系与架构、Linux应用/驱动开发、Qt、通信协议及开发工具链等核心内容。

