/dev/input/event0,event1...
,應用程序直接open
文件,read
數據便可/dev/input
,可看到有不少輸入設備文件topeet@ubuntu:~$ cd /dev/input/ topeet@ubuntu:/dev/input$ ls by-id by-path event0 event1 event2 event3 event4 js0 mice mouse0 mouse1 mouse2
/sys/class/input
下查看設備詳細信息topeet@ubuntu:/dev/input$ cd /sys/class/input/ topeet@ubuntu:/sys/class/input$ ls event0 event1 event2 event3 event4 input0 input1 input2 input3 input4 js0 mice mouse0 mouse1 mouse2 topeet@ubuntu:/sys/class/input$ cd event0 topeet@ubuntu:/sys/class/input/event0$ ls dev device power subsystem uevent topeet@ubuntu:/sys/class/input/event0$ cd device topeet@ubuntu:/sys/class/input/event0/device$ ls capabilities device event0 id modalias name phys power properties subsystem uevent uniq # 能夠看到,event0設備對應電源鍵 topeet@ubuntu:/sys/class/input/event0/device$ cat name Power Button
/dev/input/event0
)是在handler層完成/* 功能:分配一個input_dev對象 參數:無 返回值:input_dev對象指針 注意:此處在函數內部實現了內存分配,後續須要本身手動釋放內存 */ struct input_dev *input_allocate_device(void); /* 功能:釋放分配的input_dev對象內存 參數:無 返回值:無 */ void input_free_device(struct input_dev *dev);
__set_bit(unsigned long nr, volatile void * addr); /*例如*/ __set_bit(EV_KEY, inputdev->evbit); //表示當前設備可產生按鍵數據 __set_bit(KEY_POWER, inputdev->keybit); //表示當前設備可以產生power按鍵數據
/* 功能:註冊input device對象 參數:產生的input device對象指針 返回值:註冊成功返回0 注意:當註冊失敗的時候須要及時釋放掉申請的input device對象的內存(建議使用goto語句集中處理錯誤) */ int input_register_device(struct input_dev *dev);
#include <linux/init.h> #include <linux/module.h> #include <linux/input.h> struct input_dev *inputdev; static int __init simple_input_init(void) { int ret; /*分配input_dev對象*/ inputdev = input_allocate_device(); if(inputdev == NULL) { printk(KERN_ERR "input_allocate_device error\n"); return -ENOMEM; } /*初始化input_dev對象*/ __set_bit(EV_KEY, inputdev->evbit); //表示當前設備可產生按鍵數據 __set_bit(KEY_POWER, inputdev->keybit); //表示當前設備可以產生power按鍵數據 /*註冊input_dev對象*/ ret = input_register_device(inputdev); if(ret != 0) { printk(KERN_ERR "input_register_device error\n"); goto err_0; } return 0; err_0: input_free_device(inputdev); //注意釋放內存 return ret; } static void __exit simple_input_exit(void) { input_unregister_device(inputdev); input_free_device(inputdev); } module_init(simple_input_init); module_exit(simple_input_exit); MODULE_LICENSE("GPL");
[root@iTOP-4412]# ls /dev/input/ event0 [root@iTOP-4412]# insmod simple_input.ko [ 40.811369] simple_input: loading out-of-tree module taints kernel. [ 40.817391] input: Unspecified device as /devices/virtual/input/input1 [root@iTOP-4412]# ls /dev/input/ event0 event1
int get_irqno_from_node(void) { //經過節點鏈表獲取設備樹中的節點 struct device_node *np = of_find_node_by_path("/key_init_node"); if(np) { printk("find node ok\n"); } else { printk("find node failed\n"); } //經過節點獲取中斷號碼 int irqno = irq_of_parse_and_map(np, 0); printk("irqno = %d\n", irqno); return irqno; }
ret = request_irq(irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key3_eint10", NULL);
/*阻塞方式的中斷回調函數(傳統)*/ irqreturn_t key_irq_handler(int irqno, void *devid) { int read_val; //保存引腳讀取結果 printk("------%s------\n", __FUNCTION__); //read_val = readl((key_dev->key_reg_base) && (1<<2)); read_val = readl(key_dev->reg_base + 4) & (1 << 2); if(read_val > 0) { printk("key up\n"); //按鍵擡起 key_dev->event.code = KEY_ENTER;//假設其爲回車鍵 key_dev->event.value = 0; } else { printk("key down\n"); //按鍵按下 key_dev->event.code = KEY_ENTER; key_dev->event.value = 1; } /*有按鍵數據到達,喚醒進程*/ wake_up_interruptible(&key_dev->wq_head); /*設置標誌位,表示有數據到達*/ key_dev->is_have_data = 1; return IRQ_HANDLED; }
/* 功能:輸入子系統向核心層上報數據 參數: 參數1:input device對象 參數2:上報的數據類型 EV_KEY 按鍵 EV_ABS 參數3:上報的具體數據是啥;例如按鍵,對應的按鍵值是啥,電源鍵仍是音量鍵... 參數4:值是什麼;例如按下爲1,擡起爲0 */ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); /* 功能:同步數據,即告訴核心層數據上報完畢 參數:input device對象 */ static inline void input_sync(struct input_dev *dev);
#include <linux/init.h> #include <linux/module.h> #include <linux/input.h> #include <linux/of_irq.h> #include <linux/of.h> #include <linux/interrupt.h> #define GPX1CON_REG 0x11000C20 int irqno; //記錄中斷號碼 void *reg_base; struct input_dev *inputdev; int get_irqno_from_node(void) { int irqno; //經過節點鏈表獲取設備樹中的節點 struct device_node *np = of_find_node_by_path("/key_init_node"); if(np) { printk("find node ok\n"); } else { printk("find node failed\n"); } //經過節點獲取中斷號碼 irqno = irq_of_parse_and_map(np, 0); printk("irqno = %d\n", irqno); return irqno; } irqreturn_t input_key_irq_handler(int irqno, void *devid) { int read_val; //保存引腳讀取結果 printk("------%s------\n", __FUNCTION__); //read_val = readl((key_dev->key_reg_base) && (1<<2)); read_val = readl(reg_base + 4) & (1 << 2); if(read_val) { /*按鍵擡起*/ input_event(inputdev, EV_KEY, KEY_POWER, 0); /*結束上報*/ input_sync(inputdev); } else { input_event(inputdev, EV_KEY, KEY_POWER, 1); input_sync(inputdev); } return IRQ_HANDLED; } static int __init simple_input_init(void) { int ret; /*分配input_dev對象*/ inputdev = input_allocate_device(); if(inputdev == NULL) { printk(KERN_ERR "input_allocate_device error\n"); return -ENOMEM; } /*初始化input_dev對象*/ __set_bit(EV_KEY, inputdev->evbit); //表示當前設備可產生按鍵數據 __set_bit(KEY_POWER, inputdev->keybit); //表示當前設備可以產生power按鍵數據 /*註冊input_dev對象*/ ret = input_register_device(inputdev); if(ret != 0) { printk(KERN_ERR "input_register_device error\n"); goto err_0; } /*硬件初始化*/ irqno = get_irqno_from_node(); ret = request_irq(irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key3_eint10", NULL); if(ret != 0) { printk("request irq error\n"); goto err_1; } reg_base = ioremap(GPX1CON_REG, 8); return 0; err_1: input_unregister_device(inputdev); err_0: input_free_device(inputdev); //注意釋放內存 return ret; } static void __exit simple_input_exit(void) { iounmap(reg_base); free_irq(irqno, NULL); input_unregister_device(inputdev); input_free_device(inputdev); } module_init(simple_input_init); module_exit(simple_input_exit); MODULE_LICENSE("GPL");
[root@iTOP-4412]# insmod simple_input.ko [ 9900.667121] input: Unspecified device as /devices/virtual/input/input2 [ 9900.682842] find node ok [ 9900.683976] irqno = 94 [root@iTOP-4412]# [ 9911.710065] ------input_key_irq_handler------ [ 9911.834621] ------input_key_irq_handler------ [ 9912.706581] ------input_key_irq_handler------
input_event
結構體去獲取核心層上報的數據,此處與驅動中input_event
函數的參數含義相同struct input_event { struct timeval time; //時間戳,表示啥時候獲取的數據 __u16 type; //數據類型 __u16 code; //鍵 __s32 value; //值 };
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/input.h> int main(int argc, char *agvr[]) { int fd; int rd_ret; struct input_event event_data; fd = open("/dev/input/event1", O_RDWR); if(fd < 0) { printf("open error\n"); return -1; } while(1) { rd_ret = read(fd, &event_data, sizeof(event_data)); if(rd_ret < 0) { printf("read error\n"); return -1; } if(event_data.type == EV_KEY) { if(event_data.code == KEY_POWER) { if(event_data.value > 0) { printf("__APP_USER__: power key pressed\n"); } else { printf("__APP_USER__: power key up\n"); } } } } close(fd); return 0; }
[root@iTOP-4412]# ./simple_input_test [ 1896.714053] ------input_key_irq_handler------ __APP_USER__: power key pressed [ 1897.863495] ------input_key_irq_handler------ __APP_USER__: power key up [ 1899.033702] ------input_key_irq_handler------ __APP_USER__: power key pressed [ 1900.166032] ------input_key_irq_handler------ __APP_USER__: power key up
鍵盤:鍵盤會產生鍵值(按鍵值),在linux系統中不一樣按鍵都對應一個具體的值node
#define KEY_MUTE 113 #define KEY_VOLUMEDOWN 114 //音量- #define KEY_VOLUMEUP 115 //音量+ #define KEY_POWER 116 //電源按鍵觸摸屏:產生座標(絕對座標),有一個原點(0,0)linux
#define ABS_X 0x00 //x值 #define ABS_Y 0x01 //y值 #define ABS_PRESSURE 0x18 define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ #define ABS_MT_POSITION_X 0x35 /* Center X touch position */ #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */鼠標:產生座標(相對座標)shell
#define REL_X 0x00 #define REL_Y 0x01
struct input_dev { const char *name; //系統中給用戶看的信息 const char *phys; const char *uniq; struct input_id id; unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; //位表,每1個位表明不一樣類型的數值,描述輸入設備可以產生什麼類型的數據 unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; struct device dev; //繼承device對象 struct list_head h_list; // struct list_head node; //表示節點 };
EV_KEY
表示,其餘數據類型表示以下#define EV_SYN 0x00 //同步數據類型 #define EV_KEY 0x01 //按鍵數據類型 #define EV_REL 0x02 //相對座標 #define EV_ABS 0x03 //絕對座標 #define EV_MSC 0x04 //雜項 #define EV_SW 0x05 //開關 #define EV_LED 0x11 //LED指示 #define EV_SND 0x12 //聲音 #define EV_REP 0x14 #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 #define EV_MAX 0x1f #define EV_CNT (EV_MAX+1)
struct input_event { struct timeval time; //時間戳,表示啥時候獲取的數據 __u16 type; //數據類型 __u16 code; //鍵 __s32 value; //值 };
__set_bit(int nr, volatile unsigned long *addr);
/dev/input
目錄下是沒法查看其具體屬性的,所以咱們可在/sys/class/input/
下查看具體輸入設備信息topeet@ubuntu:/sys/class$ cd /sys/class/input/ topeet@ubuntu:/sys/class/input$ ls event0 event1 event2 event3 event4 input0 input1 input2 input3 input4 js0 mice mouse0 mouse1 mouse2 topeet@ubuntu:/sys/class/input$ cd event0 topeet@ubuntu:/sys/class/input/event0$ ls dev device power subsystem uevent topeet@ubuntu:/sys/class/input/event0$ cd device # 此處能夠在設備信息下面看到具體設備信息,如name,phys,uniq,id等,此處正好對應3.3.1中input_dev對象中的成員 topeet@ubuntu:/sys/class/input/event0/device$ ls capabilities device event0 id modalias name phys power properties subsystem uevent uniq topeet@ubuntu:/sys/class/input/event0/device$ cat name Power Button
/sys/class/input/
查看到具體信息/*添加輸入設備信息*/ inputdev->name = "simple input key"; inputdev->phys = "key/input/key0"; inputdev->uniq = "key0 for exynos4412"; inputdev->id.bustype = BUS_HOST; //與CPU鏈接的總線類型,此處GPIO控制 inputdev->id.vendor = 0x1234; //供應商編號 inputdev->id.product = 66; //產品編號 inputdev->id.version = 0x0001; //版本號碼
/*input_dev對象中的一個成員數組*/ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //相似long buffer[24] /*可見最多有0x2ff+1位(768種)*/ #define KEY_MAX 0x2ff #define KEY_CNT (KEY_MAX+1)
#define KEY_POWER 116
,即用keybit數組中的第116位來表示電源鍵,也就是說要把第116位給置1方法一:編程
__set_bit(KEY_POWER, inputdev->keybit); //表示當前設備可以產生power按鍵數據 /*源碼分析*/ static inline void __set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = BIT_MASK(nr); //先移位置1 unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); //再經過指針偏移將具體數組元素賦值 *p |= mask; } #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG)方法二:ubuntu
inputdev->keybit[BIT_WORD(KEY_POWER)] |= BIT_MASK(KEY_POWER); //另外一種設置位的方法
通用類型:數組
/* 功能:輸入子系統向核心層上報數據 參數: 參數1:input device對象 參數2:上報的數據類型 EV_KEY 按鍵 EV_ABS 絕對座標(屏幕) 參數3:上報的具體數據是啥;例如按鍵,對應的按鍵值是啥,電源鍵仍是音量鍵... 參數4:值是什麼;例如按下爲1,擡起爲0 */ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);專有類型:其實就是調用通用類型app
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_KEY, code, !!value); //!!確保上報數據類型爲0或者1 } static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_REL, code, value); } static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_ABS, code, value); } static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_FF_STATUS, code, value); } static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_SW, code, !!value); }
1.中斷號碼框架
2.按鍵狀態(經過GPIO數據寄存器得到)函數
3.按鍵的值(code)源碼分析