《Linux Device Drivers》第三章 字符设备驱动程序——note

本文介绍了Linux设备驱动的基础概念,包括主设备号和次设备号的作用及分配方式,并详细阐述了file_operations结构及其成员函数,如open、read、write等,还讲解了字符设备的注册流程。
  • 主设备号和次设备号
    • 那些名称被称为特殊文件、设备文件,或者简单称之为文件系统树的节点,它们通常位于/dev目录
    • 通常而言,主设备号标识设备对应的驱动程序
    • 一个主设备号对应一个驱动程序
    • 设备编号的内部表达
      • dev_t(<linux/types.h>)
      • dev_t是一个32位的数,12位表示主设备号,其余20位表示次设备号
      • <linux/kdev_t.h>
        • MAJOR(dev_t dev);
        • MINOR(dev_t dev);
        • MKDEV(int major, int minor);
    • 分配和释放设备编号
      • <linux/fs.h>
        • int register_chrdev_region(dev_t first, unsigned int count, char *name);
        • int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
        • void u nregister_chrdev_resion(dev_t first, unsigned int count);
    • 动态分配主设备号
      • 驱动程序应该始终使用alloc_chrdev_region而不是register_chrdev_region函数
      • 缺点是:由于分配的主设备号不能保证始终一致,所以无法预先创建设备节点
      • /proc/devices
      • 分配主设备号的最佳方式
        • 默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地

  • 一些重要的数据结构
    • 三个重要的内核数据结构
      • file_operations
      • file
      • inode
    • 文件操作
      • file_operations结构用来将驱动程序操作连接到设备编号
      • <linux/fs.h>
      • file_operations结构或者指向这类结构的指针称为fops
        • 每个字段必须指向驱动程序中实现特定操作的函数
      • struct module *owner
      • loff_t (*llseek) (struct file *, loff_t, int);
      • ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
      • ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
      • ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
      • ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t *);
      • int (*readdir) (struct file *, ,void *, filldir_t);
      • unsigned int (*poll) (struct file *, struct poll_table_struct *);
      • int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
      • int (*mmap) (struct file *, struct vm_area_struct *);
      • int (*open) (struct inode *, struct file *);
      • int (*flush) (struct file *);
      • int (*release) (struct inode *, struct file *);
      • int (*fsync) (struct file *, struct dentry *, int);
      • int (*aio_fsync) (struct kiocb *, int);
      • int (*fasync) (int, struct file *, int);
      • int (*lock) (struct file *, int, struct file_lock *);
      • ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
      • ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
      • ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
      • ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
      • unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
      • int (*check_flags) (int)
      • int (*dir_notify) (struct file *, unsigned long);
    • file结构
      • file结构代表一个打开的文件
      • <linux/fs.h>
      • 与用户空间程序中的FILE没有任何关联
      • 指向struct file的指针通常被称为file或filp
      • 字段
        • mode_t f_mode
        • loff_t f_pos
        • unsigned int f_flags
        • struct file_operations *f_op
        • void *private_data
        • struct dentry *f_dentry
    • inode结构
      • 内核用inode结构在内部表示文件
      • 对单个文件,可能会有许多个表示打开的文件描述符的file结构
      • 字段
        • dev_t i_rdev
        • struct cdev *i_cdev
  • 字符设备的注册
    • <linux/cdev.h>
    • 获取一个独立的cdev结构
      • struct cdev *my_cdev = cdev_alloc();
      • my_cdev->ops = &my_fops;
      • owner
    • struct cdev *cdev_alloc(void);
    • void cdev_init(struct cdev *cdev, struct file_operations *fops);
    • int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
    • void cdev_del(struct cdev *dev);
  • open和release
    • open方法
      • 主要工作
        • 检查设备特定的错误
        • 如果设备是首次打开,则对其进行初始化
        • 如有必要,更新f_op指针
        • 分配并填写置于flip->private_data里的数据结构
      • int (*open)(struct inode *inode, struct file *filp);
      • <linux/kernel.h>
        • container_of(pointer, continer_type, container_field);
    • release方法
      • 主要工作
        • 释放由open分配的、保存在filp->private_data中的所有内容
        • 在最后一次关闭操作时关闭设备
  • scull的内存使用
    • <linux/slab.h>
      • void *kmalloc(size_t size, int flags);
      • void kfree(void *ptr);
      • 不应该将非kmalloc返回的指针传递给kfree
      • 将NULL指针传递给kfree是合法的
  • read和write
    • ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
    • ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
    • 内核代码不能直接引用用户空间的指针
      • 随着驱动程序所运行的架构的不同或者内核配置的不同,在内核模式中运行时,用户空间的指针可能是无效的
      • 即使该指针在内核空间中代表相同的东西,但用户空间的内存是分页的,而在系统调用被调用时,涉及到的内存可能根本不在RAM中
      • 用户空间的指针由用户程序提供,该程序可能存在缺陷或者是个恶意程序
    • 访问用户空间的缓冲区应始终通过内核提供的专用函数完成
      • <asm/uaccess.h>
      • unsigned long copy_to_user(void __user *to, const void * from, unsigned long count)
      • unsigned long copy_from_user(void *to, const void __user *from, unsigned long count);
    • 访问用户空间的任何函数都必须是可重入的,必须能和其他驱动程序函数并发执行,必须处于能够合法休眠的状态
    • read方法
      • 如果返回值等于传递read系统调用的count参数,则说明所请求的字节数传输成功完成了
      • 如果返回值是正的,但比count小,说明部分数据传输成功
      • 如果返回值为,则表示已经到达了文件尾
      • 负值意味着发生了错误,该值指明了发生了什么错误,错误码在<linux/error.h>中定义
      • 现在还没有数据,但以后可能会有
    • write方法
    • readv和writev
      • ssize_t (*readv) (struct file * filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
      • ssize_t (*writev) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
      • iovec结构
        • void __user *iov_base;
        • __kernel_size_t iov_len
<think>嗯,用户这次的需求很明确:查找HT16C22芯片的驱动程序。从引用内容来看,用户似乎有嵌入式开发或Linux内核调试的背景(之前引用涉及dmesg缓冲区分析、内核崩溃堆栈等),但当前问题更偏向硬件驱动层面。 用户提到的HT16C22是Holtek的LED驱动控制器,这类驱动通常不会独立存在于主线内核。需要优先考虑三个方向:一是芯片厂商是否提供闭源驱动,二是Linux社区是否有非官方移植,三是通过类似驱动(如ht16k33)修改适配。 注意到用户前文引用过dmesg调试缓冲区的内容,可能是在实际设备调试中遇到了驱动缺失问题。回复时需强调硬件兼容性检查和内核日志分析的重要性,因为这类小众芯片的驱动安装往往依赖手动加载和故障排查。 特别要提醒用户注意版本匹配——用户引用[3]显示其设备使用Intel DG2显卡(属于较新硬件),若系统内核版本过旧可能加剧驱动适配难度。回复中给出具体命令示例(如modprobe/dmesg)会更实用,毕竟用户展示过分析内核日志的能力。 最后补充替代方案时,要区分“完全兼容的官方驱动”和“功能相近的可修改驱动”,避免用户浪费时间在不兼容的代码上。Holtek官网数据手册的提示必不可少,这类芯片往往需要根据时序图自行编写GPIO控制逻辑。 </think>根据您的需求,HT16C22 是 Holtek(盛群半导体)生产的 LED 驱动控制器芯片。以下是查找该驱动程序的系统化步骤和解决方案: --- ### **1. 确认芯片用途与驱动类型** - **HT16C22** 是用于驱动 LED 点阵/LCD 显示的控制芯片,通常通过 I²C 或 SPI 接口通信。 - 在 Linux 内核中,此类设备驱动属于 **字符设备驱动**(Character Device Driver)或 **帧缓冲驱动**(Framebuffer Driver)。 --- ### **2. 官方驱动查找** - **Holtek 官网**: 访问 [Holtek 开发者资源页面](https://www.holtek.com.cn/development),在搜索栏输入 **HT16C22**,检查是否有: - Linux 内核驱动源码(`.c` 文件) - 示例代码或应用笔记(Application Note) > 📌 注意:部分厂商仅提供裸机代码,需自行移植到 Linux。 --- ### **3. Linux 内核源码检索** - **内核内置驱动**: 执行以下命令搜索内核源码(需下载内核源码): ```bash grep -r "HT16C22" /path/to/linux-kernel-source/drivers/ ``` 重点检查目录: ``` drivers/leds/ # LED 设备驱动 drivers/video/fbdev/ # 帧缓冲驱动 drivers/staging/ # 未完全集成的驱动 ``` - **兼容驱动可能性**: 若内核无直接支持,可尝试通用驱动: - **`leds-ht16k33`**:内核内置的 Holtek LED 驱动(支持 HT16K33),需检查是否兼容 HT16C22。 - 对比两者数据手册的寄存器映射和通信协议。 --- ### **4. 社区与第三方资源** - **GitHub/GitLab 搜索**: 关键词:`HT16C22 driver linux site:github.com` 例如:[ht16c22-linux-driver 仓库示例](https://github.com/search?q=ht16c22+driver&type=repositories)(需验证可用性) - **嵌入式社区**: - 在 **Stack Overflow**、**Linux 内核邮件列表**(LKML)或 **Raspberry Pi 论坛** 发起询问。 - 参考类似芯片的驱动实现(如 [HT16K33 驱动源码](https://github.com/torvalds/linux/blob/master/drivers/leds/leds-ht16k33.c))。 --- ### **5. 手动编写驱动(最终方案)** 若以上方法无效,需基于芯片手册开发简易驱动: ```c #include <linux/i2c.h> #include <linux/module.h> #define HT16C22_ADDR 0x70 // I²C 地址(根据电路调整) static int ht16c22_probe(struct i2c_client *client) { // 初始化芯片:设置振荡器、显示亮度等 u8 init_cmds[] = {0x21, 0x81, 0xEF}; i2c_master_send(client, init_cmds, sizeof(init_cmds)); return 0; } static struct i2c_driver ht16c22_driver = { .driver = { .name = "ht16c22" }, .probe = ht16c22_probe, }; module_i2c_driver(ht16c22_driver); ``` **关键步骤**: 1. 实现 `ioctl()` 或 `sysfs` 接口控制显示内容。 2. 添加 **设备树(Device Tree)** 配置,声明 I²C 地址和中断引脚(若有)。 --- ### **6. 加载与测试驱动** ```bash # 编译驱动 make -C /lib/modules/$(uname -r)/build M=$(pwd) modules # 加载模块 sudo insmod ht16c22.ko # 查看内核日志 dmesg | grep ht16c22 # 确认驱动是否成功初始化[^1] ``` --- ### 📚 **相关问题** 1. **如何在 Linux 中为 I²C 设备编写字符设备驱动?** 2. **如何将自定义驱动添加到内核编译配置?** 3. **调试 Linux 内核驱动时,`dmesg` 输出的关键错误有哪些?** 4. **Holtek HT16C22 与 HT16K33 驱动的兼容性如何验证?** [^1]: 内核调试环形缓冲区记录了驱动初始化状态和硬件交互细节,可通过 `dmesg` 命令实时监控设备加载情况。 [^2]: 驱动开发中需关注函数偏移地址(如 `dhdsdio_htclk+0x28`),这有助于定位崩溃点的反汇编代码。 [^3]: 使用 `lspci -nnk` 可确认设备是否被正确识别并绑定到驱动(如本例中的 VFIO 驱动绑定)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值