当前位置: 首页 > news >正文

驱动基础知识速览(迅为RK3568文档)

一、基础知识

  1. 编译代码的三个环境变量,ARCHCROSS_COMPILEPATH分别表示架构、工具链、路径
  2. 驱动模块传参,module_parammodule_param_arraymodule_param_string传递基本数据类型、数组和字符串
  3. 内核模块导出EXPORT_SYMBOL可以导出变量、函数
  4. 驱动模块编译进内核,当前驱动源码下的Makefile和Kconfig,前者使用obj-x += x.o指定编译方法,后者编写config选项在menu中添加配置选项,上级目录的Kconfig通过source包含子目录的Kconfig
  5. 字符设备注册流程
alloc_chrdev_region(*dev, baseminor, count, *name);        // 动态分配设备号,分配的第一个设备号为dev,分配count个设备号,主设备号相同,次设备号从baseminor开始,name表示名称
cdev_init(*cdev, *fops);                                   // 绑定cdev和fops
cdev_add(*cdev, devt, count);                              // 注册一个cdev结构体,对应设备号devt,关联数量count,一般每个设备分配一个cdev即count=1
class_create(THIS_MODULE, *name);                          // 创建一个名称为name的类
device_create(*class, NULL, devt, NULL, *name);            // 在class类下创建一个device,没有父设备,设备号是devt,节点名称name,udev机制会自动识别并创建设备节点 
  1. fops如何传递数据?file结构体中有成员private_data,可以通过它传递
  2. MISC设备,主设备号10,注册方法misc_register,miscdevice结构体中有成员fops,在驱动入口中注册MISC设备,并在fops中实现IO操作
  3. 错误处理,IS_ERR判断函数返回的指针是不是错误指针,如果是错误指针通过PTR_ERR返回错误码,goto语法实现了不同阶段的不同错误处理

二、高级字符设备

  1. IO模型的分类:阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO
  2. 阻塞IO
wait_queue_head_t head;
init_waitqueue_head(&head);              // 初始化等待队列头
wait_event_interrupt(head, cond);        // 条件不满足进入可中断的阻塞等待
wait_up_interrupt(&head);                // 唤醒等待队列上等待的休眠进程
  1. 非阻塞IO:读写时如果资源无效则直接返回-EAGAIN,并且非阻塞访问时需要带有flag,O_NONBLOCK
  2. IO多路复用,实现驱动中的poll函数,驱动poll函数中对可能引起状态变化的等待队列调用poll_wait,之后返回是否能对设备进行无阻塞读写访问的掩码
  3. 信号驱动IO,实现驱动中的fasync函数,直接调用fasync_helper函数,当资源准备好的时候使用kill_fasync发送指定的信号
  4. 异步IO,实现aio函数,略
  5. 定时器的使用
struct timer_list my_timer;
void my_timer_callback(struct timer_list *t);
timer_setup(&my_timer, my_timer_callback, 0);
mod_timer(&my_timer, jifiies+msecs_to_jiffies(1000));
del_timer(&my_timer);
  1. Linux内核打印
dmesg | grep "something"
cat /proc/kmsg              // 开一个终端长时间读信息
echo 7 4 1 7 > /proc/sys/kernel/printk
  1. lseek,实现对三个参数的处理,SEEK_SETSEEK_CURSEEK_END
  2. ioctl
_IO(type, nr);                // type为一个ASCII码、nr表示一个cmd的索引
_IOR(type, nr, size);          //size为一个结构体/变量
_IOW(type, nr, size);
_IOWR(type, nr, size);
_IOC_TYPE(cmd);            // 根据cmd返回type
_IOC_NR(cmd);              // 根据cmd返回nr
_IOC_DIR(cmd);             // 根据CMD返回方向,与_IOC_READ/_IOC_WRITE相与
access_ok(addr, size);      // 检测用户空间的内存块是否可用 
  1. 分支预测优化,likely表示表达式为真的可能性更大、unlikely表示表达式为真的可能性更小
  2. 驱动的其他调试方式
dump_stack();        // 打印内核调用堆栈、打印函数调用关系
WARN_ON(cond);        // cond为真时,打印内核调用堆栈,打印函数调用关系
BUGON(cond);         // cond为真时,抛出oops,打印函数调用堆栈和错误信息
panic(fmt);          // 系统死机,打印韩式调用堆栈和寄存器值

三、中断

  1. 中断子系统
用户层:用户驱动,使用通用的中断API注册中断和中断服务函数
核心层:向上提供通用API
硬件相关层:包含特定处理器相关的代码、以及中断控制器的驱动代码
硬件层:包含中断控制器和CPU
  1. 函数使用
request_irq(irq, handler, flags, *name, *dev);    // 请求一个中断号并关联对应的中断处理函数
gpio_to_irq(gpio);    // 将GPIO的引脚编号转化为中断请求号
free_irq(irq, *dev_id);  // 释放中断号、中断处理程序和设备标识
irq_handler的返回值,IRQ_NONE中断函数未处理该中断、IRQ_HANDLED中断函数处理了该中断、IRQ_WAKE_THREAD中断函数处理了该中断并请求唤醒一个内核线程进行后续处理
  1. 中断关键结构体
struct irq_desc{        // 描述中断handle_irq;        // 中断处理函数*action;           // 中断action*kstat_irqs;       // 中断统计信息irq_data;          // 中断相关数据irq_common_data;   // 中断通用数据
};
struct irqaction{handler;          // 中断处理函数*dev_id;          // 传递的设备相关信息*next;            // irq行为的链表下一项flags;            // 中断标志、触发方式、共享标志
};
  1. tasklet软中断(中断下半部分的一种实现,运行在软中断的上下文中)
struct tasklet_struct my_tasklet;
void my_tasklet_func(unsigned long data);
tasklet_init(&my_tasklet, my_tasklet_func, 0);
tasklet_enable(&my_tasklet);
tasklet_disable(&my_tasklet);
tasklet_schedule(&my_taslket);      // 放在中断处理函数返回前执行,当内核准备返回用户空间时,检测到有调度的tasklet,执行对应的tasklet_func
  1. 软中断(不建议直接使用软中断、因为已经被内核用了、网络数据收发、块设备、定时器等)
在TASKLET_SOFTIRQ对应的枚举中加入自定义的软中断或直接使用现成软中断
open_softirq(int nr, void (*action)(struct softirq_action *));        // 注册软中断和对应的处理函数
void raise_softirq(unsigned int nr);            // 触发软中断
  1. Tasklet如何使用软中断,在softirq初始化的时候,注册软中断和对应的处理函数,该处理函数遍历一个tasklet的链表指向对应的中断处理后半部分,当调用tasklet_schedule时,将tasklet加入链表并触发软中断,严格来说是挂起,真实执行需要等到中断处理函数完成,程序即将从内核返回用户空间。
  2. 工作队列,工作队列运行在内核进程上下文,可以休眠,由工作队列线程管理执行工作队列的工作队列项
// 使用共享工作队列
struct work_struct workqueue_work;
void work(struct work_struct *work);
INIT_WORK(&workqueue_work, work);
schedule_work(&workqueue_work);      // 提交工作项到内核共享工作队列中
// 使用自定义工作队列
struct workqueue_struct* workqueue = create_workqueue("test");    // 创建自定义工作队列
void work(struct work_struct *work);
struct work_struct workqueue_work;
INIT_WORK(&workqueue_work, work);
queue_work(workqueue, &workqueue_work);
// 延时执行的工作队列项
INIT_DELAYED_WORK(&workqueue_work, work);      // 初始化延时的工作项
queue_delayed_work(workqueue, &workqueue_work, 3*HZ);  // 提交延时工作项到自定义工作队列
// 如何使用工作队列传参
// 工作队列work_struct结构体放在一个结构体中,使用container_of函数根据work_struct结构体寻找到传参结构体
// 并发管理的工作队列
struct workqueue_struct* workqueue = alloc_workqueue("test", WQ_UNBOUND, 0);
  1. 中断线程化,将中断下半部分交给一个内核线程执行,通过函数request_threded_irq(irq, handler, thread_fn, irqflags, *devname, *dev_id);请求并注册一个线程化的中断处理函数,中断上半部分返回IRQ_WAKE_THREAD将中断处理推迟到下半部分
http://www.hskmm.com/?act=detail&tid=21108

相关文章:

  • 学习笔记-析合树
  • CSPJ2025模拟赛
  • java代码审计-Shiro认证授权
  • CF868F题解
  • ThinkPHP反序列化分析
  • AT_iroha2019_day4_l 题解
  • AT_abc290_f 题解
  • 一张图读懂绿电直连系统架构 - 智慧园区
  • P5469 [NOI2019] 机器人 题解
  • 题解:P14080 [GESP202509 八级] 最小生成树
  • 实用指南:Spring Cloud Gateway 实战:全局过滤器日志统计与 Prometheus + Grafana 接口耗时监控
  • GTSAM 中自定义因子(Custom Factor)的详解和实战示例 - 指南
  • AtCoder AGC073 A 题解
  • CF506C Mr. Kitayuta vs. Bamboos 51nod1457 小K vs. 竹子 题目分析
  • 北京 意大利 硕士学签/D签 vfs代办 材料清单
  • temp
  • 我有园子了
  • 使用 Jenkins 的流水线方案实施 CI/CD
  • 加密的病例单
  • 详细介绍:视频融合平台EasyCVR构筑智慧交通可视化管理与智能决策中枢
  • docker 在x86上build arm 镜像
  • 9.29软工
  • 不一样的.NET烟火,基于Roslyn的开源代码生成器
  • 复刻江协旋钮控制模块
  • 告别硬编码!5个让Web自动化脚本更稳定的定位策略
  • 从零开始,使用Idea工具搭建一个springboot项目
  • 最优/极值问题的算法选择
  • 第三方控件库的添加和使用
  • C4NR PVP服务器1.2 天穹炮塔更新
  • 树形dp [JOI Open 2020] 发电站 / Power Plant