Linux設備驅動之input子系統 作者:武漢華嵌嵌入式培訓中心 講師 李家凱 對于輸入類設備如鍵盤、鼠標、觸摸屏之類的Linux驅動,內核提供input子系統,使得這類設備的處理變得非常便捷。總體上來講,input子系統由三部分組成: 事件驅動<——>input核心<——>設備驅動。 其中事件驅動負責與用戶程序打交道,諸如設備節點/dev之類的,都由他負責,我們在寫驅動時就不用實現這個了;設備驅動負責與硬件設備打交道,這里的交互很簡單,只需要讀取相關硬件的數據,然后拋給input核心就可以了; 舉個例子,以按鍵key為例,定義了設備設備號、按鍵值,配置管腳和中斷方式,然后申請中斷。在中斷服務函數中,讀取對應管腳值,用input_report函數發送給input核心,并用input_sync通知發送結束即可。另外,在模塊初始化時,定義一個input_dev的結構體,這個input_dev是input子系統設備驅動端的核心數據結構,由于輸入設備多種多樣,就是通過這個結構體告訴核心你的輸入設備類型。 其中的兩個重要成員,這些宏具體在linux/input.h中定義。 一個是,evbit,代表事件類型的指示位,常用的如 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 狀態 另一個是keybit,代表鍵值代碼 其他的還有 relbit 相對定位 absbit 絕對定位 mscbit Mouse Systems Corporation,大意是一些廠商使用了5字節的串口鼠標協議,但微軟使用了一種三字節協議,于是廠商造串口鼠標時,讓設備有兩種工作模式,一種是MSC模式,一種是微軟的模式 ledbit 鍵盤指示燈事件的指示位 sndbit 鍵盤發出聲音的指示位 ffbit force feedback,強制反饋設備 swbit switch,設備切換時產生的事件 下面就分別給出驅動代碼和測試程序,以供參考。 驅動代碼: #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; } // 打開設備 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; } // 關閉設備 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; } 本例代碼為武漢華嵌驅動課程實際教學案例,編譯后,加載驅動,然后運行測試程序,當按下某個按鍵時,可以在終端上看到對應的打印信息。 |