Linux設備驅動(dòng)之input子系統 作者:武漢華嵌嵌入式培訓中心 講師 李家凱 對于輸入類(lèi)設備如鍵盤(pán)、鼠標、觸摸屏之類(lèi)的Linux驅動(dòng),內核提供input子系統,使得這類(lèi)設備的處理變得非常便捷?傮w上來(lái)講,input子系統由三部分組成: 事件驅動(dòng)<——>input核心<——>設備驅動(dòng)。 其中事件驅動(dòng)負責與用戶(hù)程序打交道,諸如設備節點(diǎn)/dev之類(lèi)的,都由他負責,我們在寫(xiě)驅動(dòng)時(shí)就不用實(shí)現這個(gè)了;設備驅動(dòng)負責與硬件設備打交道,這里的交互很簡(jiǎn)單,只需要讀取相關(guān)硬件的數據,然后拋給input核心就可以了; 舉個(gè)例子,以按鍵key為例,定義了設備設備號、按鍵值,配置管腳和中斷方式,然后申請中斷。在中斷服務(wù)函數中,讀取對應管腳值,用input_report函數發(fā)送給input核心,并用input_sync通知發(fā)送結束即可。另外,在模塊初始化時(shí),定義一個(gè)input_dev的結構體,這個(gè)input_dev是input子系統設備驅動(dòng)端的核心數據結構,由于輸入設備多種多樣,就是通過(guò)這個(gè)結構體告訴核心你的輸入設備類(lèi)型。 其中的兩個(gè)重要成員,這些宏具體在linux/input.h中定義。 一個(gè)是,evbit,代表事件類(lèi)型的指示位,常用的如 EV_SYN 0x00 同步事件 EV_KEY 0x01 按鍵事件 EV_REL 0x02 相對坐標 EV_ABS 0x03 絕對坐標 EV_MSC 0x04 其它 EV_LED 0x11 LED EV_SND 0x12 聲音 EV_REP 0x14 Repeat EV_FF 0x15 力反饋 EV_PWR 電源 EV_FF_STATUS 狀態(tài) 另一個(gè)是keybit,代表鍵值代碼 其他的還有 relbit 相對定位 absbit 絕對定位 mscbit Mouse Systems Corporation,大意是一些廠(chǎng)商使用了5字節的串口鼠標協(xié)議,但微軟使用了一種三字節協(xié)議,于是廠(chǎng)商造串口鼠標時(shí),讓設備有兩種工作模式,一種是MSC模式,一種是微軟的模式 ledbit 鍵盤(pán)指示燈事件的指示位 sndbit 鍵盤(pán)發(fā)出聲音的指示位 ffbit force feedback,強制反饋設備 swbit switch,設備切換時(shí)產(chǎn)生的事件 下面就分別給出驅動(dòng)代碼和測試程序,以供參考。 驅動(dòng)代碼: #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 定義key對應的GPIO #define GPF0 (S3C2410_GPF(0)) #define GPF1 (S3C2410_GPF(1)) #define GPF2 (S3C2410_GPF(2)) #define GPF4 (S3C2410_GPF(4)) #define KEY_NUM 4 struct input_dev *key_dev; static struct key_info { int irq_no; int pin; int pin_setting; int key_no; char *name; }key_info_tab[KEY_NUM]= { {IRQ_EINT0,GPF0,S3C2410_GPF0_EINT0,1,"key_1"}, {IRQ_EINT1,GPF1,S3C2410_GPF1_EINT1,2,"key_2"}, {IRQ_EINT2,GPF2,S3C2410_GPF2_EINT2,3,"key_3"}, {IRQ_EINT4,GPF4,S3C2410_GPF4_EINT4,4,"key_4"}, }; // 中斷處理程序 static irqreturn_t hq_eint_key(int irq,void *dev_id) { if(irq==17) { input_report_key(key_dev,KEY_1,s3c2410_gpio_getpin(GPF1)); } else if(irq==48) { input_report_key(key_dev,KEY_4,s3c2410_gpio_getpin(GPF4)); } else if(irq==18) { input_report_key(key_dev,KEY_2,s3c2410_gpio_getpin(GPF2)); } else if(irq==16) { input_report_key(key_dev,KEY_0,s3c2410_gpio_getpin(GPF0)); } input_sync(key_dev);input_sync(key_dev); return IRQ_HANDLED; } // 打開(kāi)設備 static int hq_key_open(struct input_dev *dev) { int i; int err=0; for(i=0;i err=request_irq(key_info_tab.irq_no,hq_eint_key,IRQF_SAMPLE_RANDOM,key_info_tab.name,(void *)(&key_info_tab)); if(err) { return -1; } return 0; } // 關(guān)閉設備 static void hq_key_release(struct input_dev *dev) { int i; for(i=0;i free_irq(key_info_tab.irq_no,(void *)(&key_info_tab)); } } //模塊初始化 static int __init hq_key_init(void) { int err; key_dev=input_allocate_device(); if(!key_dev){ return -ENOMEM; } set_bit(EV_KEY,key_dev->evbit); set_bit(KEY_1,key_dev->keybit); set_bit(KEY_2,key_dev->keybit); set_bit(KEY_3,key_dev->keybit); set_bit(KEY_4,key_dev->keybit); key_dev->name="input_key_demo"; key_dev->dev.init_name="input_key"; key_dev->open=hq_key_open; key_dev->close=hq_key_release; err=input_register_device(key_dev); if(err){ return err; } return 0; } // 模塊卸載 static void __exit hq_key_exit(void) { input_unregister_device(key_dev); } MODULE_AUTHOR("www.embedhq.org"); MODULE_LICENSE("Dual BSD/GPL"); module_init(hq_key_init); module_exit(hq_key_exit); 測試程序: #include #include #include #include #include int main(void ) { int fd; int key_value,i=0,count; struct input_event ev_key; fd=open("/dev/input/event0",0666); if(fd<0){ perror("open device"); exit(1); } while(1){ count=read(fd,&ev_key,sizeof(struct input_event)); for(i=0;i<(int)count/sizeof(struct input_event);i++) { if(EV_KEY==ev_key.type) { int num=ev_key.code%10-1; printf("type:%d,code:%d ,value:%d\n key%d pressed!\n",ev_key.type,ev_key.code,ev_key.value,num); } if(EV_SYN==ev_key.type) printf("syn event\n"); } } close(fd); return 0; } 本例代碼為武漢華嵌驅動(dòng)課程實(shí)際教學(xué)案例,編譯后,加載驅動(dòng),然后運行測試程序,當按下某個(gè)按鍵時(shí),可以在終端上看到對應的打印信息。 |