LINUX設備驅動之輸入子系統(二)

二.Input handler的註冊

Input device的註冊中存在下列疑問:

1,  匹配devhandler時,input_handler_list上的handler是什麼時候掛上去的呢?

2,  匹配成功後會調用相應handlerconnect函數,此函數做了什麼事?

帶着這兩個疑問,我們以鍵盤爲例進行分析。

在系統啓動初始化vtyvty_init函數,ttyvty部分內容將在以後分析)時會調用kbd_init()進行鍵盤初始化,kbd_init函數定義於drivers/char/keyboard.c

1403       int __init kbd_init(void)

1404       {

1405              int i;

1406              int error;

1407      

1408               for (i = 0; i < MAX_NR_CONSOLES; i++) {

1409                     kbd_table[i].ledflagstate = KBD_DEFLEDS;

1410                     kbd_table[i].default_ledflagstate = KBD_DEFLEDS;

1411                            kbd_table[i].ledmode = LED_SHOW_FLAGS;

1412                     kbd_table[i].lockstate = KBD_DEFLOCK;

1413                     kbd_table[i].slockstate = 0;

1414                     kbd_table[i].modeflags = KBD_DEFMODE;

1415                     kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;

1416              }

1417      

1418              error = input_register_handler(&kbd_handler);

1419              if (error)

1420                     return error;

1421      

1422              tasklet_enable(&keyboard_tasklet);

1423              tasklet_schedule(&keyboard_tasklet);

1424      

1425              return 0;

1426       }

1408~1416行初始化kbd_table數組,這部分與tty內容相關,以後再分析。

1418行,這裏我們終於看到調用input_register_handler函數註冊kbd_handlerkbd_handler定義如下:

1394       static struct input_handler kbd_handler = {

1395              .event             = kbd_event,

1396              .connect  = kbd_connect,

1397              .disconnect     = kbd_disconnect,

1398              .start              = kbd_start,

1399              .name             = "kbd",

1400              .id_table  = kbd_ids,

1401       };

我們看到id_table指向kbd_ids數組,kbd_ids定義如下:

1378       static const struct input_device_id kbd_ids[] = {

1379              {

1380                       .flags = INPUT_DEVICE_ID_MATCH_EVBIT,

1381                       .evbit = { BIT_MASK(EV_KEY) },

1382               },

1383      

1384              {

1385                       .flags = INPUT_DEVICE_ID_MATCH_EVBIT,

1386                       .evbit = { BIT_MASK(EV_SND) },

1387               },

1388      

1389              { },    /* Terminating entry */

1390       };

從這個id_table看到,只要input_dev設置了其evbit字段支持EV_KEYEV_SND都會匹配到hnadler,回想一下在鍵盤驅動中創建input_dev後調用的atkbd_set_device_attrs設置input_dev的函數中有下列語句:

       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |

              BIT_MASK(EV_MSC);

說明at鍵盤的input_dev會匹配到這個hnadler

我們接着看input_register_handler函數:

1600       int input_register_handler(struct input_handler *handler)

1601       {

1602              struct input_dev *dev;

1603              int retval;

1604      

1605              retval = mutex_lock_interruptible(&input_mutex);

1606              if (retval)

1607                     return retval;

1608      

1609              INIT_LIST_HEAD(&handler->h_list);

1610      

1611                     if (handler->fops != NULL) {

1612                     if (input_table[handler->minor >> 5]) {

1613                            retval = -EBUSY;

1614                            goto out;

1615                     }

1616                     input_table[handler->minor >> 5] = handler;

1617              }

1618      

1619              list_add_tail(&handler->node, &input_handler_list);

1620      

1621              list_for_each_entry(dev, &input_dev_list, node)

1622                     input_attach_handler(dev, handler);

1623      

1624              input_wakeup_procfs_readers();

1625      

1626       out:

1627              mutex_unlock(&input_mutex);

1628              return retval;

1629       }

1605行獲得互斥信號量,1609行初始化handler h_list字段,這個h_list指向的鏈表用於存放input_handle

1611~1617行,我們的kbd_handler沒有定義fops函數,所以這段代碼不會執行,不過我們還是看一下,這裏的input_table數組是一個struct input_handler結構數組,根據設備的次設備號除以32的值爲下標的元素爲input_handler爲什麼是32?輸入子系統最多支持256個設備,而input_handler結構的數組最多處理8類事件,所以分給每一類的次設備號段分配32個。

1619行把handler鏈接到input_handler_list尾部。

1621~1622handler去匹配input_dev_list上的input_devinput_attach_handler函數在上面已經分析過,再最後如果匹配成功會調用handlerconnect函數,對於這個kbd_handler相應的函數爲kbd_connect,看一下這個函數:

1314       static int kbd_connect(struct input_handler *handler, struct input_dev *dev,

1315                            const struct input_device_id *id)

1316       {

1317              struct input_handle *handle;

1318              int error;

1319              int i;

1320      

1321              for (i = KEY_RESERVED; i < BTN_MISC; i++)

1322                     if (test_bit(i, dev->keybit))

1323                            break;

1324      

1325              if (i == BTN_MISC && !test_bit(EV_SND, dev->evbit))

1326                     return -ENODEV;

1327      

1328              handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);

1329              if (!handle)

1330                     return -ENOMEM;

1331      

1332              handle->dev = dev;

1333              handle->handler = handler;

1334              handle->name = "kbd";

1335      

1336              error = input_register_handle(handle);

1337              if (error)

1338                     goto err_free_handle;

1339      

1340              error = input_open_device(handle);

1341              if (error)

1342                     goto err_unregister_handle;

1343      

1344              return 0;

1345      

1346       err_unregister_handle:

1347              input_unregister_handle(handle);

1348       err_free_handle:

1349              kfree(handle);

1350              return error;

1351       }

1321~1326行判斷dev->keybit,如果屬於例外的情況,則不再往下走,返回-ENODEV

1328行,爲handle分配內存空間,1332~1334行初始化handle的部分字段。

1336行調用input_register_handle註冊handle,看一下這個函數:

1671       int input_register_handle(struct input_handle *handle)

1672       {

1673              struct input_handler *handler = handle->handler;

1674              struct input_dev *dev = handle->dev;

1675              int error;

1676      

1677              /*

1678              * We take dev->mutex here to prevent race with

1679              * input_release_device().

1680              */

1681              error = mutex_lock_interruptible(&dev->mutex);

1682              if (error)

1683                     return error;

1684              list_add_tail_rcu(&handle->d_node, &dev->h_list);

1685              mutex_unlock(&dev->mutex);

1686      

1687              /*

1688              * Since we are supposed to be called from ->connect()

1689              * which is mutually exclusive with ->disconnect()

1690              * we can't be racing with input_unregister_handle()

1691              * and so separate lock is not needed here.

1692              */

1693              list_add_tail(&handle->h_node, &handler->h_list);

1694      

1695              if (handler->start)

1696                     handler->start(handle);

1697      

1698              return 0;

1699       }

16841693handle分別鏈接到dev->h_listhandler->h_list

1695~1696行,如果handlerstart函數存在,則調用它。對於這個kbd_handler相應的函數爲kbd_start,看一下這個函數:

1364       static void kbd_start(struct input_handle *handle)

1365       {

1366              unsigned char leds = ledstate;

1367      

1368              tasklet_disable(&keyboard_tasklet);

1369              if (leds != 0xff) {

1370                     input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));

1371                     input_inject_event(handle, EV_LED, LED_NUML,    !!(leds & 0x02));

1372                     input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & 0x04));

1373                     input_inject_event(handle, EV_SYN, SYN_REPORT, 0);

1374              }

1375              tasklet_enable(&keyboard_tasklet);

1376       }

禁止keyboard_tasklet,初始化鍵盤的三個led燈及SYN_REPORT,然後啓用keyboard_tasklet,這個keyboard_tasklet也是對led燈進行操作的,函數如下

1032              DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);

1014       static void kbd_bh(unsigned long dummy)

1015       {

1016              struct list_head *node;

1017              unsigned char leds = getleds();

1018      

1019              if (leds != ledstate) {

1020                     list_for_each(node, &kbd_handler.h_list) {

1021                            struct input_handle *handle = to_handle_h(node);

1022                            input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));

1023                            input_inject_event(handle, EV_LED, LED_NUML,    !!(leds & 0x02));

1024                            input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & 0x04));

1025                            input_inject_event(handle, EV_SYN, SYN_REPORT, 0);

1026                     }

1027              }

1028      

1029              ledstate = leds;

1030       }

input_register_handle函數返回後,第1340行調用input_open_device函數,這個函數執行後相應的handle就能接受設備上報的事件。函數如下:

0421       int input_open_device(struct input_handle *handle)

0422       {

0423              struct input_dev *dev = handle->dev;

0424              int retval;

0425      

0426              retval = mutex_lock_interruptible(&dev->mutex);

0427              if (retval)

0428                     return retval;

0429      

0430              if (dev->going_away) {

0431                     retval = -ENODEV;

0432                     goto out;

0433              }

0434      

0435              handle->open++;

0436      

0437              if (!dev->users++ && dev->open)

0438                     retval = dev->open(dev);

0439      

0440              if (retval) {

0441                     dev->users--;

0442                     if (!--handle->open) {

0443                            /*

0444                            * Make sure we are not delivering any more events

0445                            * through this handle

0446                            */

0447                            synchronize_rcu();

0448                     }

0449              }

0450      

0451       out:

0452              mutex_unlock(&dev->mutex);

0453              return retval;

0454       }

增加handleopen計數,如果handle->dev->users不爲0,則自增1,如果爲0並且handle->devopen函數存在則會調用它,對於前面分析的atkbd,相應的handle->devopen函數不存在。

至此,input handlerinput handler的註冊都分析完了,接着將分析事件處理部分內容。

發佈了18 篇原創文章 · 獲贊 2 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章