混杂设备驱动模型
Linux中断处理
按键驱动硬件操作实现
中断分层设计
按键定时器去抖
阻塞型驱动程序设计
/—————————————————————————/
混杂设备驱动模型
Linux中,存在一类字符设备,其拥有相同主设备号(10),但次设备号不同,称混杂设备(miscdevice)。所有混杂设备形成一链表,对设备访问时据次设备号找到相应设备。
使用struct miscdevice来描述一混杂设备
struct miscdevice {
int minor; /* 次设备号*/
const char *name; /* 设备名*/
const struct file_operations *fops; /*文件操作*/
struct list_head list;
struct device *parent;
struct device *this_device;
};
注册int misc_register(struct miscdevice * misc);
注销int misc_deregister(struct miscdevice *misc);
/——————/
Linux中断处理
如设置该位,表为快速中断处理程序;如没设置该位,表为慢速中断处理程序
IRQF_SHARED(SA_SHIRQ)表该中断为多设备共享(如上图多个struct irqaction,一设备中断,会顺序执行多个irqaction的handler,只需在handler中检测,实际未发生中断则立即退出即可)
快/慢速中断主要区别:快速中断保证中断处理原子性,而慢速中断则不保证。换句话说即开启中断标志位在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型中断所打断;而调用慢速中断处理时,其它类型的中断仍可以得到服务。
中断处理程序特别之处是在中断上下文中运行,它的行为受到某些限制:
1不能使用可能引起阻塞的函数(特别是快速中断处理程序)
2不能使用可能引起调度的函数
中断处理程序:检测设备是否真正产生中断——清除中断标志(CPU内部中断标志无需…,外部如DM9000中中断标志需…)——相应硬件操作
void free_irq(unsigned int irq, void *dev_id)
/——————/
按键驱动硬件操作实现
/——————/
中断分层设计
慢速中断:不同中断类型,产生中断嵌套;相同中断类型,被忽略
快速中断:全被忽略
故会出现中断丢失,为避免该情况,引入中断分层概念(上半部:和硬件相关、下半部:和硬件无关)
上半部:当中断发生时,它进行相应硬件读写,并登记该中断。通常由中断处理程序充当上半部
下半部:在系统空闲时对上半部登记的中断进行后续处理
中断分层方式:软中断、tasklet、工作队列(使用最广泛)
工作队列是一种将任务推后执行的形式,他把推后的任务交由一内核线程去执行。这
样下半部会在进程上下文执行,它允许重新调度甚至睡眠。每个被推后的任务叫做工
作,由这些工作组成的队列称工作队列
Linux内核使用struct workqueue_struct来描述一工作队列:
创建工作INIT_WORK
提交工作queue_work
定义好的工作队列keventd_wq中:schedule_work
——————/
按键定时器去抖
按键去抖方法主要有二种,一种是硬件电路去抖;另一种是软件延时去抖。而延时又分二种,一种是for循环等待,另一种是定时器延时。在OS中,由于效率方面原因,只能使用定时器。
Linux内核使用struct timer_list描述一定时器:
初始化定时器:
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
注册定时器:add_timer(&buttons_timer);
启动定时器:mod_timer(&buttons_timer, jiffies + (HZ /10));
——————/
阻塞型驱动程序设计
定义等待队列wait_queue_head_t my_queue
初始化等待队列init_waitqueue_head(&my_queue)
定义+初始化等待队列DECLARE_WAIT_QUEUE_HEAD(my_queue)
wait_event(queue,condition)
当condition为真,立即返回;否则进入TASK_UNINTERRUPTIBLE模式睡眠,并挂在queue所指定队列上
wait_event_interruptible(queue,condition)当condition为真,立即返回;否则进入
TASK_INTERRUPTIBLE睡眠,并……
int wait_event_killable(queue, condition)当condition为真,立即返回;否则进入
TASK_KILLABLE睡眠,并……
wake_up(wait_queue_t *q)从等待队列唤醒状态为TASK_UNINTERRUPTIBLE,
TASK_INTERRUPTIBLE,TASK_KILLABLE 所有进程
wake_up_interruptible(wait_queue_t *q)唤醒状态为TASK_INTERRUPTIBLE进程
Linux中断处理
按键驱动硬件操作实现
中断分层设计
按键定时器去抖
阻塞型驱动程序设计
/—————————————————————————/
混杂设备驱动模型
Linux中,存在一类字符设备,其拥有相同主设备号(10),但次设备号不同,称混杂设备(miscdevice)。所有混杂设备形成一链表,对设备访问时据次设备号找到相应设备。
使用struct miscdevice来描述一混杂设备
struct miscdevice {
int minor; /* 次设备号*/
const char *name; /* 设备名*/
const struct file_operations *fops; /*文件操作*/
struct list_head list;
struct device *parent;
struct device *this_device;
};
注册int misc_register(struct miscdevice * misc);
注销int misc_deregister(struct miscdevice *misc);
/——————/
Linux中断处理
__irq_svc: //中断统一入口,entry-armv.S
irq_handler //标号
.macro irq_handler
arch_irq_handler_default
.endm
.macro arch_irq_handler_default //entry-macro-multi.S
1: get_irqnr_and_base r0, r2, r6, lr //获取中断号,架构不同,方法异
bne asm_do_IRQ
.endm
asm_do_IRQ (unsigned int irq, struct pt_regs *regs) //中断号
handle_IRQ
generic_handle_irq(irq);
struct irq_desc *desc = irq_to_desc(irq); //根据irq获得irq_desc
generic_handle_irq_desc(irq, desc);
desc->handle_irq(irq, desc); //调用irq对应的中断处理函数
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev) //返回0表成功,或返回一错误码
void *dev_id共享中断时使用
IRQF_DISABLED(SA_INTERRUPT)
如设置该位,表为快速中断处理程序;如没设置该位,表为慢速中断处理程序
IRQF_SHARED(SA_SHIRQ)表该中断为多设备共享(如上图多个struct irqaction,一设备中断,会顺序执行多个irqaction的handler,只需在handler中检测,实际未发生中断则立即退出即可)
快/慢速中断主要区别:快速中断保证中断处理原子性,而慢速中断则不保证。换句话说即开启中断标志位在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型中断所打断;而调用慢速中断处理时,其它类型的中断仍可以得到服务。
中断处理程序特别之处是在中断上下文中运行,它的行为受到某些限制:
1不能使用可能引起阻塞的函数(特别是快速中断处理程序)
2不能使用可能引起调度的函数
中断处理程序:检测设备是否真正产生中断——清除中断标志(CPU内部中断标志无需…,外部如DM9000中中断标志需…)——相应硬件操作
void free_irq(unsigned int irq, void *dev_id)
/——————/
按键驱动硬件操作实现
#define S3C2410_CPUIRQ_OFFSET (16)
#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
#define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */
即IRQ_EINT0为中断号16,中断号0-15留给软中断(2440)
6410留0-31给软中断
request_irq(IRQ_EINT0,……)时所初始化为irq_desc[16],实际发生中断时:
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp //entry-macro.S
ldr \irqnr, [ \base, #INTOFFSET ]
adds \irqnr, \irqnr, #IRQ_EINT0 //加16
/——————/
中断分层设计
慢速中断:不同中断类型,产生中断嵌套;相同中断类型,被忽略
快速中断:全被忽略
故会出现中断丢失,为避免该情况,引入中断分层概念(上半部:和硬件相关、下半部:和硬件无关)
上半部:当中断发生时,它进行相应硬件读写,并登记该中断。通常由中断处理程序充当上半部
下半部:在系统空闲时对上半部登记的中断进行后续处理
中断分层方式:软中断、tasklet、工作队列(使用最广泛)
工作队列是一种将任务推后执行的形式,他把推后的任务交由一内核线程去执行。这
样下半部会在进程上下文执行,它允许重新调度甚至睡眠。每个被推后的任务叫做工
作,由这些工作组成的队列称工作队列
Linux内核使用struct workqueue_struct来描述一工作队列:
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq;
struct list_head list;
const char *name; /*workqueue name*/
int singlethread;
int freezeable; /* Freeze threads during suspend */
int rt;
};
Linux内核使用struct work_struct来描述一工作项:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; //最重要
};
typedef void (*work_func_t)(struct work_struct *work);
创建工作队列create_workqueue
创建工作INIT_WORK
提交工作queue_work
#include <linux/init.h>
#include <linux/module.h>
struct workqueue_struct *my_wq;
struct work_struct *work1;
struct work_struct *work2;
void work1_func(struct work_struct *work)
{
printk("this is work1->\n");
}
void work2_func(struct work_struct *work)
{
printk("this is work2->\n");
}
int init_que(void)
{
my_wq = create_workqueue("my_que");
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work1, work1_func);
queue_work(my_wq,work1);
work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work2, work2_func);
queue_work(my_wq,work2);
return 0;
}
void clean_que()
{
}
module_init(init_que);
module_exit(clean_que);
MODULE_LICENSE("GPL");
大多数情况下, 驱动并不需自己建立工作队列,只需定义工作, 然后将工作提交到内核已
定义好的工作队列keventd_wq中:schedule_work
int init_que(void)
{
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work1, work1_func);
schedule_work(work1);
work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work2, work2_func);
schedule_work(work2);
return 0;
}
——————/
按键定时器去抖
按键去抖方法主要有二种,一种是硬件电路去抖;另一种是软件延时去抖。而延时又分二种,一种是for循环等待,另一种是定时器延时。在OS中,由于效率方面原因,只能使用定时器。
Linux内核使用struct timer_list描述一定时器:
struct timer_list {
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function)(unsigned long);
unsigned long data;
int slack;
};
定义定时器变量:struct timer_list buttons_timer;
初始化定时器:
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
注册定时器:add_timer(&buttons_timer);
启动定时器:mod_timer(&buttons_timer, jiffies + (HZ /10));
——————/
阻塞型驱动程序设计
定义等待队列wait_queue_head_t my_queue
初始化等待队列init_waitqueue_head(&my_queue)
定义+初始化等待队列DECLARE_WAIT_QUEUE_HEAD(my_queue)
wait_event(queue,condition)
当condition为真,立即返回;否则进入TASK_UNINTERRUPTIBLE模式睡眠,并挂在queue所指定队列上
wait_event_interruptible(queue,condition)当condition为真,立即返回;否则进入
TASK_INTERRUPTIBLE睡眠,并……
int wait_event_killable(queue, condition)当condition为真,立即返回;否则进入
TASK_KILLABLE睡眠,并……
wake_up(wait_queue_t *q)从等待队列唤醒状态为TASK_UNINTERRUPTIBLE,
TASK_INTERRUPTIBLE,TASK_KILLABLE 所有进程
wake_up_interruptible(wait_queue_t *q)唤醒状态为TASK_INTERRUPTIBLE进程