建站平台营销,做支付网站,合肥外贸网站推广,seo有什么作用一、设备驱动程序 在上一篇随笔中已经分析#xff0c;linux输入子系统分为设备驱动层、核心层和事件层。要利用linux内核中自带的输入子系统实现一个某个设备的操作#xff0c;我们一般只需要完成驱动层的程序即可#xff0c;核心层和事件层内核已经帮我们做好了。因此这篇随… 一、设备驱动程序 在上一篇随笔中已经分析linux输入子系统分为设备驱动层、核心层和事件层。要利用linux内核中自带的输入子系统实现一个某个设备的操作我们一般只需要完成驱动层的程序即可核心层和事件层内核已经帮我们做好了。因此这篇随笔主要介绍按键操作设备驱动层的代码。 1.1设备驱动入口函数 在设备驱动入口函数中我们需要做的事1分配一个input_dev 结构体 2设置这个input_dev 结构体 3调用input_register_device注册这个input_dev 4完成硬件相关操作如注册中断处理函数添加定时器等 static int button_init(void)
{int i;int err;/* 1. 分配一个 input_dev 结构体*/button_dev input_allocate_device();/* 2. 设置 *//* 2.1 能产生哪类事件 */set_bit(EV_KEY, button_dev-evbit);/* 2.2 能产生这类事件下的哪些操作: L S ENTER LEFTSHIT*/set_bit(KEY_L, button_dev-keybit);set_bit(KEY_S, button_dev-keybit);set_bit(KEY_ENTER, button_dev-keybit);set_bit(KEY_LEFTSHIFT, button_dev-keybit);/* 3. 注册 */input_register_device(button_dev);/* 4. 硬件相关操作*/gpkcon ioremap(GPKCON_PA, 4); //io口映射gpkdat ioremap(GPKDAT_PA, 4);gpndat ioremap(GPNDAT_PA, 4);init_timer(button_timer); // 初始化定时器button_timer.function button_timer_function;// 指定定时器的处理函数add_timer(button_timer); // 添加定时器for (i0; i4; i){err request_irq(button_irqs[i].irq, buttons_interrupt, button_irqs[i].flags,button_irqs[i].name,(void *)button_irqs[i]);if (err)break;} return 0;
} 1.1.1在初始化iinput_dev结构体过程 主要对其中的如下数组做了初始化从而来确定该设备支持哪些事件支持哪些操作。 初始化时首先要确定设备能够产生哪一类事件 事件的类型如下 事件类型的设置主要对evbit[]数组中的相应位做设置 set_bit(EV_KEY, button_dev-evbit); 然后再确定设备支持该类事件下的哪些操作 例如在相对坐标事件下可以支持如下操作 在本次按键驱动程序中按键设备产生的事件自然是按键事件按键事件支持KEY_L、KEY_S、KEY_ENTER、KEY_LEFTSHIFT 4个操作分别对应 L S entershift set_bit(KEY_L, button_dev-keybit); set_bit(KEY_S, button_dev-keybit); set_bit(KEY_ENTER, button_dev-keybit set_bit(KEY_LEFTSHIFT, button_dev-keybit); 1.1.2 注册输入设备input_register_device() 这个函数在上一篇博客中已经做了简要分析这里在提一下input_register_device()中做了哪些事 1 设置同步事件、清除KEY_RESERVED、清除bitmasks中没有提到的位 2 初始化定时器确定定时器的处理函数。这里定时器与重复上报事件有关注意在事件类型中有EV_REP事件设置这个事件在ev_bit中的相应位就可以重复上报事件。 3 设置getkeycode 和 setkeycode 函数 4 device_add input_dev包含的device结构注册到Linux设备模型中在sysfs文件系统中可以看到增加了设备input1 5list_add_tail 在上一篇博客中已经介绍了 6遍历iinput_handler_list 中的事件处理器 input_handler 与input_handler 进行匹配 连接操作。 具体的连接操作在下边分析。 1.1.3 硬件相关操作 定时器、IO映射、注册中断 2. 中断处理函数 static void button_timer_function(unsigned long data)
{int num;int tmp;struct button_irq_desc* pindesc irq_pdesc;if (! pindesc)return;num pindesc-number; tmp *gpndat;input_event(button_dev, EV_KEY,button_irqs[num].key_val , !(tmp (1num)));input_sync(button_dev);return;
}
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{/* 10 ms 则 hz/100 100 ms 则 hz/10*/irq_pdesc (struct button_irq_desc *)dev_id;mod_timer(button_timer, jiffiesHZ/100);return IRQ_RETVAL(IRQ_HANDLED);
} 中断处理函数包括两部分定时中断用于消除按键抖动 按键中断处理函数主要用来调 整定时器事件 当有按键中断发生时我们需要上报事件 input_event(button_dev, EV_KEY,button_irqs[num].key_val , !(tmp (1num))); input_sync(button_dev); 2.1.1 input_event 上报事件函数 void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{unsigned long flags;// 判断是否支持这种事件if (is_event_supported(type, dev-evbit, EV_MAX)) {spin_lock_irqsave(dev-event_lock, flags);add_input_randomness(type, code, value);// 执行事件处理函数input_handle_event(dev, type, code, value);spin_unlock_irqrestore(dev-event_lock, flags);}
} static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{int disposition INPUT_IGNORE_EVENT;.....switch (type) {case EV_KEY:if (is_event_supported(code, dev-keybit, KEY_MAX) !!test_bit(code, dev-key) ! value) {if (value ! 2) {__change_bit(code, dev-key);if (value)input_start_autorepeat(dev, code);elseinput_stop_autorepeat(dev);}disposition INPUT_PASS_TO_HANDLERS;}break;if (disposition ! INPUT_IGNORE_EVENT type ! EV_SYN)dev-sync false;if ((disposition INPUT_PASS_TO_DEVICE) dev-event)dev-event(dev, type, code, value);if (disposition INPUT_PASS_TO_HANDLERS)input_pass_event(dev, type, code, value);
} 大概可以分析出事件处理函数要么执行input_dev下的event 要么执行input_handler下的event函数至于执行哪一个和disposition这个变量有关。 需要注意的是如果执行的是input_dev下边的event那么应该只会执行一个event函数如果要是执行的是input_handler下的event函数那么会执行input_pass_event 函数。这个函数中通过input_dev-h_list链表上挂的input_handler结构体找到与之匹配的input_handler。然后执行input_handler下的event函数。 如果input_dev与多个input_handler事件处理器匹配了那么当设备驱动上报事件时并且要执行input_handler中的event函数那么会依次执行这些匹配好的input_handler的event函数。在接下的实验中可以看到这一点。 二、程序执行部分过程分析 2.1 input_dev与 input_handler 的连接 因为我们的程序中已经注册了evdev_handler事件处理器在上一篇博客中已经分析过了input_register_handler和 kbd_handler 事件处理器在keyboard.c 文件中 kbd_init--input_register_handler(kbd_handler)) 当在驱动程序中input_register_device后设备驱动和这两个事件处理器进行匹配和连接操作。 2.1.1 button_dev 和 evdev_handler 连接 匹配完成之后会执行连接操作连接操作执行的是evdev_handler-connet 函数这个函数已经在前一篇博客中分析了这里只简述函数执行过程和效果。 1 分配evdev 结构体 2 设置evdev 结构体下边的 handle 和 dev 3 注册evdev 下边的handle 在上一篇博客中已经分析了这个handle是连接input_dev 和 input_handler 的桥梁 4 添加设备device_addevdev-dev 执行完这个函数应该可以看到在/dev/input/下出现even0 设个设备 可以看到主设备号为13 次设备号为64 这里次设备号64minor 因为evdev_handler 还没有和任何设备建立连接所以在input_table[] 数组中还没存放任何evdev结构体因此minor的值为0 故此设备号为64064。 2.1.2 button_dev 和 kbd_handler 的匹配过程 static int kbd_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{struct input_handle *handle;int error;handle kzalloc(sizeof(struct input_handle), GFP_KERNEL);if (!handle)return -ENOMEM;handle-dev dev;handle-handler handler;handle-name kbd;error input_register_handle(handle);if (error)goto err_free_handle;error input_open_device(handle);if (error)goto err_unregister_handle;return 0;err_unregister_handle:input_unregister_handle(handle);err_free_handle:kfree(handle);return error;
} 这里的连接过程比较简单没有上一个连接过程那么复杂但是可以看到主要的数据结构input_handle并没有少因为这个结构体是input_dev和input_handler的联系桥梁复杂的数据结构之间的关系可以使input_dev 可以访问到input_handle从而访问到input_handler。在事件上报函数中分析过怎样从input_dev找到input_handler 2.2 事件上报函数分析 前文已经提到了button_dev 与 evdev_handler 和 kbd_handler 匹配连接成功那么在事件上报的时候就会分别执行这两个事件处理器中的event函数。 2.2.1 evdev-event 函数 这个函数已经在上一篇博客中提前分析过了。 2.2.2 kbd-event 函数 static void kbd_event(struct input_handle *handle, unsigned int event_type,unsigned int event_code, int value)
{/* We are called with interrupts disabled, just take the lock */spin_lock(kbd_event_lock);if (event_type EV_MSC event_code MSC_RAW HW_RAW(handle-dev))kbd_rawcode(value);if (event_type EV_KEY)kbd_keycode(event_code, value, HW_RAW(handle-dev));spin_unlock(kbd_event_lock);tasklet_schedule(keyboard_tasklet);do_poke_blanked_console 1;schedule_console_callback();
} 三、实验效果 3.1 执行hexdump /dev/input/event0 依次按下 l s enter 对应的按键l 对应0x26 s 对应0x1f enter对应0x1c 3.2 执行cat /dev/tty1 并且将标准输入重定向到/dev/tty1 依次按下 l s enter 对应的按键 可以看到依次按下l s enter 后执行了ls 命令 同时需要强调的是上边的两个命令的执行在不同的shell 下但是只按下了一次l s enter后分别在两个shell中打印了不同的结果。这就证明了之前所说的一个button_dev 与 evdev_handler 和 kbd_handler 建立了连接上报一次事件会分别调用这两个事件处理器的event 函数。 转载于:https://www.cnblogs.com/zf1-2/p/10859535.html