中断下半部:
tasklet :
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long); //下半部要执行的代码
unsigned long data; // 传递给func的参数
};
1, 初始化tasklet
tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data)
2, 在中断上半部,将tasklet加入到内核线程
tasklet_schedule(struct tasklet_struct * t)
3, 模块卸载的时候,需要从内核线程中移除tasklet
tasklet_kill(struct tasklet_struct * t)
------------------------------------------------------------------
typedef void (*work_func_t)(struct work_struct *work);
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;//下半部要执行的代码
};
1, 初始化work
INIT_WORK(struct work_struct * work, work_func_t func);
work_func_t func为结构体struct work_struct中的函数指针;
2, 在中断上半部,将work加入到内核线程
schedule_work(struct work_struct * work);
3, 模块卸载的时候,需要从内核线程中移除work
cancel_work_sync(struct work_struct * work)
工作队列(work queue)是另外一种将工作推后执行的形式,它和前面讨论的tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。
那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列。如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。
引用: https://blog.csdn.net/av_geek/article/details/41278801
一个为 struct tasklet_struct *next任务链表,一个为struct list_head entry;内核链表
驱动编写规范
设计一个对象描述所有的全局变量
//设计一个对象类型--描述所有的全局的变量
struct s5pv210_key{
__u8 have_data; //用于描述是否有数据
int major ; //记录主设备号
struct class *cls; //用于创建 类
struct device *dev; //用来创建设备文件
wait_queue_head_t wq_head; //用于实现阻塞的等待队列头
struct key_event event; //用于存放数据的包
struct work_struct work;//用于实现中断下半部
};
声明一个对象
//声明一个对象
struct s5pv210_key *key_dev;
初始化时统一申请空间(这也是好处之一)
// 0-为全局设备/数据对象分配空间
//参数2--标志--分配内存方式, GFP_KERNEL(如果当前内存不够的时候会等待)
key_dev = kzalloc(sizeof(struct s5pv210_key), GFP_KERNEL);
错误判断
if(key_d