按键驱动程序设计

混杂设备驱动模型
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进程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值