淺析linux下鍵盤設(shè)備工作和注冊流程
【淺析linux下鼠標(biāo)驅(qū)動的實現(xiàn)】
input_init()=> => class_register(&input_class);注冊input類 input_proc_init();創(chuàng)建proc下的目錄和文件 register_chrdev(INPUT_MAJOR, "input", &input_fops);注冊驅(qū)動程序到cdev_map上,以待驅(qū)動設(shè)備.
drivers\input\keyboard\pxa3xx_keypad.c為我們的keyboard設(shè)備, pxa3xx_keypad_probe=> request_irq(IRQ_ENHROT, &enhanced_rotary_interrupt, IRQF_DISABLED, "Enhanced Rotary", (void *)keypad);注冊快捷鍵中斷 request_irq(IRQ_KEYPAD, pxa3xx_keypad_interrupt, IRQF_DISABLED,pdev->name, keypad);注冊中斷 static irqreturn_t pxa3xx_keypad_interrupt(int irq, void *dev_id) { struct pxa3xx_keypad *keypad = dev_id; uint32_t kpc = keypad_readl(KPC);
if (kpc & KPC_MI) pxa3xx_keypad_scan_matrix(keypad);
if (kpc & KPC_DI) pxa3xx_keypad_scan_direct(keypad);
return IRQ_HANDLED; } 在irq中如果讀到了key,那么會直接調(diào)用 input_report_key(keypad->input_dev,lookup_matrix_keycode(keypad, row, col), new_state[col] & (1 << row)); static inline unsigned int lookup_matrix_keycode( struct pxa3xx_keypad *keypad, int row, int col) { return keypad->matrix_keycodes[(row << 3) + col]; } input_report_key(struct input_dev *dev, unsigned int code, int value) dev為input_dev設(shè)備,我們的4*4鍵盤 code為標(biāo)準(zhǔn)PC鍵盤碼值 value為按鍵動作,為1表示鍵盤按下,為0表示按鍵抬起 static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_KEY, code, !!value); } 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);//因為按鍵的存在隨機(jī)性,所以按鍵是給系統(tǒng)提供墑隨機(jī)數(shù)的好來源. 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) { ... 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);//通過異或^操作,反轉(zhuǎn)code對應(yīng)的bitmap,如果value等于2,那么將忽略該按鍵 if (value) input_start_autorepeat(dev, code);//鍵盤按下,那么開啟定時檢測,這樣可以出現(xiàn)連續(xù)輸入的效果 }
disposition = INPUT_PASS_TO_HANDLERS; } break; ... } static void input_start_autorepeat(struct input_dev *dev, int code) { if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data) { dev->repeat_key = code; mod_timer(&dev->timer,//重新啟動定時器input_repeat_key,時間間隔msecs_to_jiffies(dev->rep[REP_DELAY]) jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); } }
static void input_repeat_key(unsigned long data) { struct input_dev *dev = (void *) data; unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
if (test_bit(dev->repeat_key, dev->key) && is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
input_pass_event(dev, EV_KEY, dev->repeat_key, 2);//交給處理按鍵函數(shù)
if (dev->sync) { /* * Only send SYN_REPORT if we are not in a middle * of driver parsing a new hardware packet. * Otherwise assume that the driver will send * SYN_REPORT once it's done. */ input_pass_event(dev, EV_SYN, SYN_REPORT, 1); }
if (dev->rep[REP_PERIOD]) mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); }
spin_unlock_irqrestore(&dev->event_lock, flags); }
input_pass_event=> handle->handler->event(handle, type, code, value); 就是kbd_handler的kbd_event=>kbd_keycode=> atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNICODE, ¶m) 通知掛在keyboard鏈上所有等待鍵盤輸入的應(yīng)用程序, 通過register_keyboard_notifier()函數(shù)可以注冊到鍵盤鏈上【gliethttp.Leith】,
input_dev = input_allocate_device();申請一個input設(shè)備空間 input_dev->open = pxa3xx_keypad_open;給這個空間填充方法 input_dev->close = pxa3xx_keypad_close; input_dev->private = keypad; set_bit(EV_KEY, input_dev->evbit);//鍵按下 set_bit(EV_REL, input_dev->evbit);//鍵釋放 pxa3xx_keypad_build_keycode(keypad);//設(shè)備鍵盤映射碼 該函數(shù)將根據(jù)pxa3xx_device_keypad設(shè)備下的matrix_key_map進(jìn)行鍵控設(shè)置, pxa_set_keypad_info(&jades_keypad_info)=>將jades_keypad_info登記為pdata; #define MAX_MATRIX_KEY_NUM (8 * 8) matrix_keycodes[MAX_MATRIX_KEY_NUM];表示為8*8鍵盤 keypad->matrix_keycodes[(row << 3) + col] = code;表示第row行的第col列處按鍵,代表code編碼值,這個為我們內(nèi)部使用. set_bit(code, input_dev->keybit);//設(shè)置code為我們的鍵盤對操作系統(tǒng)可用的鍵盤值 if(pdata->direct_key_num) { for (i = 0; i < pdata->direct_key_num; i++) { set_bit(pdata->direct_key_map[i], input_dev->keybit);//快捷鍵單元 } } set_bit(KEY_POWER, input_dev->keybit);//登記電源按鍵為系統(tǒng)可見按鍵 input_register_device(input_dev);=>//注冊設(shè)該備devices_subsys總線上 int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); struct input_handler *handler; const char *path; int error;
__set_bit(EV_SYN, dev->evbit);
/* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */
init_timer(&dev->timer); if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (long) dev; dev->timer.function = input_repeat_key;//消抖處理函數(shù),采用延時消抖 dev->rep[REP_DELAY] = 500;//250; dev->rep[REP_PERIOD] = 66;//33; }
if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; //在/sys/class/input下創(chuàng)建以input0,input1為目錄名的input類型設(shè)備 snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
if (dev->cdev.dev) dev->dev.parent = dev->cdev.dev;
error = device_add(&dev->dev);//將設(shè)備登記到設(shè)備總線上,之后將以目錄和文件的形式呈現(xiàn) if (error) return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path);
error = mutex_lock_interruptible(&input_mutex); if (error) { device_del(&dev->dev); return error; }
list_add_tail(&dev->node, &input_dev_list); //將設(shè)備放到input的鏈表上,該鏈表上存放著所有input類型的dev設(shè)備對象【gliethttp.Leith】 list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler); //從input_handler_list驅(qū)動鏈表上嘗試匹配,是否有驅(qū)動該dev設(shè)備的driver驅(qū)動,如果有,那么將匹配的驅(qū)動綁定給dev設(shè)備,來驅(qū)動這個dev. input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0; }
drivers\char\keyboard.c kbd_init()=> input_register_handler(&kbd_handler); 注冊鍵盤驅(qū)動到input_handler_list鏈表上
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; int error; //看看這個咚咚,是不是在黑名單里,如果在,那么就byebye了 【gliethttp.Leith】 if (handler->blacklist && input_match_device(handler->blacklist, dev)) return -ENODEV;
id = input_match_device(handler->id_table, dev); if (!id) return -ENODEV;
error = handler->connect(handler, dev, id);//ok,找到驅(qū)動該dev的driver,那么嘗試連接 if (error && error != -ENODEV) printk(KERN_ERR "input: failed to attach handler %s to device %s, " "error: %d\n", handler->name, kobject_name(&dev->dev.kobj), error);
return error; }
kbd_connect=>input_register_handle=>input_open_device=>pxa3xx_keypad_open配置鍵盤io口
|