一.混杂设备:字符设备的一种。当主设备号相同(10),次设备号为不同为混杂设备。
1.混杂设备的描述符:设备操作都需要通过一个设备描述符来对它进行操作。
混杂设备描述符:主设备号是10
struct miscdevice {
int minor; /* 次设备号*/
const char *name; /* 设备名*/
const struct file_operations *fops; /*文件操作*/
struct list_head list;
struct device *parent;
struct device *this_device;
};
2.所有的混杂设备会形成一个链表,通过次设备号来进行操作。
3.混杂设备注册函数:int misc_register(struct miscdevice * misc)
将一个初始化好的设备描述符misc注册到系统中。
二.按键驱动的初始化,注册与注销:混杂设备描述符管理
1.给按键设备描述符的minor,name,fops三个变量进行初始化赋值
例,具体数值无要求:struct miscdevice key_miscdev = {
.minor = 200,
.name = "key",
.fops = &key_fops,
};
2.调用函数misc_register将初始化好的描述符misc进行注册
3.注销混杂设备:int misc_deregister(struct miscdevice * misc)
三.中断函数的初始化,注册,处理,注销:
1.初始函数:int request_irq(
unsigned int irq, //由硬件说明书来获取对应拐角的中断号,进行外部中断号和中断处理函数的映射
void (*handler)(int, void*, strupt_regs *), //中断处理函数
unsigned long flags, //中断类型标志,不同标识处理的时机和方式不同
const char *devname, //设备名,自己取名字就好
void *dev_id) //共享中断信号线
//返回0表示成功,或者返回一个错
2.中断处理函数:irqreturn_t key_int(int irq, void *dev_id) 没有第三个参数,
3.中断注销:void free_irq(unsigned int irq, void *dev_id)注销对应的中断号和中断信号线。
四.中断分层:工作队列,硬件操作在中断处理函数中,其他操作放工作队列
1.创建一个工作队列描述符并初始化:struct workqueue_struct *my_wq;
my_wq=create_workqueue("my_que");//工作队列描述符如下
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;};
2.创建一个工作项,并初始化:struct work_struct *work1;
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);//第二个参数为分配空间标志
INIT_WORK(work1, work1_func);//处理函数加载到工作队列节点
3.将工作节点挂载到工作队列中: queue_work(my_wq,work1);//第一个参数为工作队列,第二个为工作节点项
注:可以不用第一步创建工作队列,省略第一步,将创建的工作项挂载到内核已有的工作队列中,第三步改成挂载函数为schedule_work(work1);
五.按键定时器去抖动:
1.定义一个定时器变量:struct timer_list buttons_timer;
struct timer_list {
struct list_head entry;
struct list_head entry;
unsigned long expires;//定时时间
void (*function)(unsigned long);//定时需要的执行的函数
unsigned long data;
struct tvec_base *base;
};
2.初始化:初始化,给功能函数赋值
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function; //执行函数名
3.向内核注册一个定时器:
add_timer(&buttons_timer);
4.在需要的地方开启定时回调函数:
mod_timer(&buttons_timer, jiffies + (HZ /10));
//jiffies表示当前时间,HZ表示一秒,一百毫秒超时回调
六.多按键实现:在注册中断处理函数时,结合硬件中断号再注册一遍处理函数,
在处理函数中检测硬件寄存器看是哪个按键被触发。
七.阻塞型驱动:
1.定义一个等待队列:wait_queue_head_t key_q;
2.初始化队列:init_waitqueue_head(&my_queue);
3.挂起函数wait_event(key_q,condition);当condition为假阻塞当前进程。
4.唤醒函数wake_up(&key_q);在合适的地方(中断函数)调用此函数,唤醒阻塞的进程,使那个阻塞函数继续运行。