1.代碼
input_subsys.drv.c 在linux輸入子系統(input subsystem)之按鍵輸入和LED控制的基礎上有小改動,input_subsys_test.c不變。
input_subsys.drv.c
1 #include <linux/module.h> 2 #include <linux/version.h> 3 4 #include <linux/init.h> 5 #include <linux/fs.h> 6 #include <linux/interrupt.h> 7 #include <linux/irq.h> 8 #include <linux/sched.h> 9 #include <linux/pm.h> 10 #include <linux/sysctl.h> 11 #include <linux/proc_fs.h> 12 #include <linux/delay.h> 13 #include <linux/platform_device.h> 14 #include <linux/input.h> 15 #include <linux/irq.h> 16 17 #include <asm/gpio.h> 18 #include <asm/io.h> 19 #include <asm/arch/regs-gpio.h> 20 21 22 struct pin_desc{ 23 int irq; 24 char *name; 25 unsigned int pin; 26 unsigned int key_val; 27 }; 28 29 struct pin_desc pins_desc[4] = { 30 {IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L}, 31 {IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S}, 32 {IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER}, 33 {IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT}, 34 }; 35 36 static struct input_dev *input_subsys_dev; 37 static struct pin_desc *irq_pd; 38 static struct timer_list buttons_timer; 39 40 static irqreturn_t buttons_irq(int irq, void *dev_id) 41 { 42 /* [cgw]: 按鍵IO發生邊沿中斷時重新設置定時間隔 43 * 用於按鍵消抖 44 */ 45 irq_pd = (struct pin_desc *)dev_id; 46 buttons_timer.data = irq_pd->pin; 47 mod_timer(&buttons_timer, jiffies+USER_HZ/10); 48 return IRQ_RETVAL(IRQ_HANDLED); 49 } 50 51 static void buttons_timer_function(unsigned long data) 52 { 53 struct pin_desc * pindesc = irq_pd; 54 unsigned int pinval; 55 56 if (!pindesc) 57 return; 58 59 /* [cgw]: 獲取按鍵IO狀態 */ 60 pinval = s3c2410_gpio_getpin((unsigned int)data); 61 62 /* [cgw]: 根據按鍵IO狀態上報按鍵事件 */ 63 if (pinval) 64 { 65 /* [cgw]: 上報按鍵彈起 */ 66 input_report_key(input_subsys_dev, pindesc->key_val, 0); 67 //input_sync(input_subsys_dev); 68 } 69 else 70 { 71 /* [cgw]: 上報按鍵按下 */ 72 input_report_key(input_subsys_dev, pindesc->key_val, 1); 73 //input_sync(input_subsys_dev); 74 } 75 76 //printk("timer occur!\n"); 77 } 78 79 80 static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value) 81 { 82 #if 0 83 /* [cgw]: 根據應用程序下發的LED控制事件 84 * 亮滅LED 85 */ 86 //if (code == SND_BELL) { 87 if (code == LED_MUTE) { 88 if (value == 0xAA) { 89 /* [cgw]: 點亮 */ 90 s3c2410_gpio_setpin(S3C2410_GPF4, 0); 91 } else if (value == 0xEE) { 92 /* [cgw]: 熄滅 */ 93 s3c2410_gpio_setpin(S3C2410_GPF4, 1); 94 } 95 96 return 0; 97 } 98 #endif 99 100 switch (type) { 101 case EV_REP: 102 return 0; 103 //break; 104 105 case EV_LED: 106 if (code == LED_MUTE) { 107 if (value == 0xAA) { 108 /* [cgw]: 點亮 */ 109 s3c2410_gpio_setpin(S3C2410_GPF4, 0); 110 } else if (value == 0xEE) { 111 /* [cgw]: 熄滅 */ 112 s3c2410_gpio_setpin(S3C2410_GPF4, 1); 113 } 114 115 return 0; 116 } 117 //break; 118 119 case EV_SND: 120 return 0; 121 //break; 122 } 123 124 return -1; 125 } 126 127 int input_subsys_open(struct input_dev *dev) 128 { 129 int i, retval; 130 131 /* [cgw]: 設置按鍵IO為中斷輸入 */ 132 s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_EINT0); 133 s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2); 134 s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_EINT11); 135 s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_EINT19); 136 137 /* [cgw]: 設置LED IO為輸出,初始為熄滅LED */ 138 s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); 139 s3c2410_gpio_setpin(S3C2410_GPF4, 1); 140 141 s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); 142 s3c2410_gpio_setpin(S3C2410_GPF5, 1); 143 144 s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); 145 s3c2410_gpio_setpin(S3C2410_GPF5, 1); 146 147 /* [cgw]: 為按鍵IO分配中斷線 */ 148 for (i = 0; i < 4; i++) 149 { 150 retval = request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]); 151 } 152 153 printk("input subsys open!\n"); 154 //printk("USER_HZ: %d\n", USER_HZ); 155 156 return 0; 157 } 158 159 160 161 static int input_subsys_init(void) 162 { 163 /* [cgw]: 分配一個輸入設備 */ 164 input_subsys_dev = input_allocate_device(); 165 input_subsys_dev->name = "input_subsys_dev"; 166 167 /* [cgw]: 設置支持的事件類型 */ 168 set_bit(EV_KEY, input_subsys_dev->evbit); 169 set_bit(EV_REP, input_subsys_dev->evbit); 170 171 set_bit(EV_LED, input_subsys_dev->evbit); 172 //set_bit(EV_SND, input_subsys_dev->evbit); 173 174 /* [cgw]: 設置支持的事件碼 */ 175 set_bit(KEY_L, input_subsys_dev->keybit); 176 set_bit(KEY_S, input_subsys_dev->keybit); 177 set_bit(KEY_ENTER, input_subsys_dev->keybit); 178 set_bit(KEY_LEFTSHIFT, input_subsys_dev->keybit); 179 180 set_bit(LED_MUTE, input_subsys_dev->ledbit); 181 //set_bit(SND_BELL, input_subsys_dev->sndbit); 182 183 /* [cgw]: 分配輸入設備的open方法 */ 184 input_subsys_dev->open = input_subsys_open; 185 /* [cgw]: 分配輸入設備的event方法,用戶在應用程序write()時 */ 186 input_subsys_dev->event = event_handler; 187 188 /* [cgw]: 注冊輸入設備 */ 189 input_register_device(input_subsys_dev); 190 191 //input_subsys_dev->rep[REP_DELAY] = 250; 192 //input_subsys_dev->rep[REP_PERIOD] = 100; 193 194 /* [cgw]: 初始化定時器,用於按鍵消抖 */ 195 init_timer(&buttons_timer); 196 buttons_timer.function = buttons_timer_function; 197 add_timer(&buttons_timer); 198 199 printk("input subsys init!\n"); 200 201 return 0; 202 } 203 204 static void input_subsys_exit(void) 205 { 206 int i; 207 208 /* [cgw]: 釋放按鍵IO中斷 */ 209 for (i = 0; i < 4; i++) 210 { 211 free_irq(pins_desc[i].irq, &pins_desc[i]); 212 } 213 214 /* [cgw]: 刪除定時器 */ 215 del_timer(&buttons_timer); 216 /* [cgw]: 注銷輸入設備 */ 217 input_unregister_device(input_subsys_dev); 218 /* [cgw]: 釋放輸入設備內存空間 */ 219 input_free_device(input_subsys_dev); 220 } 221 222 module_init(input_subsys_init); 223 224 module_exit(input_subsys_exit); 225 226 MODULE_LICENSE("GPL");
2. input_subsys_drv.c, input.c, evdev.c 三者之間的關系:
input_subsys_drv.c: 負責獲取底層硬件產生的事件,如:中斷,按鍵輸入等,收集到這些事件傳遞給input.c, 並通過設置evdev.c可以支持的事件類型,和evdev.c建立連接
input.c: 輸入子系統內核,收集底層硬件發來的(如:如中斷,按鍵輸入)和用戶空間發來的(如:write,ioctl)事件,傳遞給evdev.c
evdev.c: 收集從input.c傳遞過來的事件,存儲到一個環形緩沖隊列,並產生一個異步通知,通知用戶空間讀取事件
3. 按鍵輸入(底層硬件)和LED(用戶空間)事件處理過程
3.1 按鍵輸入事件處理過程:
input_subsys_drv.c 同過外部中斷獲得按鍵的狀態,經過消抖之後,向input.c上報:
1 static void buttons_timer_function(unsigned long data) 2 { 3 struct pin_desc * pindesc = irq_pd; 4 unsigned int pinval; 5 6 if (!pindesc) 7 return; 8 9 /* [cgw]: 獲取按鍵IO狀態 */ 10 pinval = s3c2410_gpio_getpin((unsigned int)data); 11 12 /* [cgw]: 根據按鍵IO狀態上報按鍵事件 */ 13 if (pinval) 14 { 15 /* [cgw]: 上報按鍵彈起 */ 16 input_report_key(input_subsys_dev, pindesc->key_val, 0); 17 //input_sync(input_subsys_dev); 18 } 19 else 20 { 21 /* [cgw]: 上報按鍵按下 */ 22 input_report_key(input_subsys_dev, pindesc->key_val, 1); 23 //input_sync(input_subsys_dev); 24 } 25 26 //printk("timer occur!\n"); 27 }
input_report_key():
1 static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) 2 { 3 input_event(dev, EV_KEY, code, !!value); 4 }
input_event()
1 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 2 { 3 ... 4 5 switch (type) { 6 ... 7 8 case EV_KEY: 9 10 if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) 11 return; 12 13 if (value == 2) 14 break; 15 16 change_bit(code, dev->key); 17 18 if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) { 19 dev->repeat_key = code; 20 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); 21 } 22 23 break; 24 25 ... 26 27 } 28 29 .... 30 handle->handler->event(handle, type, code, value); 31 }
其中:case EV_KEY中,對按鍵連發做初步檢測,即檢測是否有按鍵的按下和彈起這兩個狀態,缺一個都不行(後面解析)。
接著就調用handle->handler->event(),實際上是調用了evdev_event();
因為
1 static struct input_handler evdev_handler = { 2 .event = evdev_event, 3 ... 4 };
evdev_event():
1 static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) 2 { 3 ... 4 /* [cgw]: 把接收到的事件加入到一個環形隊列 */ 5 do_gettimeofday(&client->buffer[client->head].time); 6 client->buffer[client->head].type = type; 7 client->buffer[client->head].code = code; 8 client->buffer[client->head].value = value; 9 client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1); 10 11 /* [cgw]: 發送一個異步通知 */ 12 kill_fasync(&client->fasync, SIGIO, POLL_IN); 13 14 /* [cgw]: 喚醒正在等待這個事件的進程 */ 15 wake_up_interruptible(&evdev->wait); 16 }
在evdev_event中發送了異步通知並喚醒了再睡眠的進程,所以在應用程序調用read時,就會獲得這個事件。
1 /* [cgw]: 異步通知產生時返回的數據 */ 2 read(fd, &buttons_event, sizeof(struct input_event));
3.1.1 按鍵連發的處理過程
首先在input_subsys_init() 使能EV_REP按鍵連發功能
1 static int input_subsys_init(void) 2 { 3 /* [cgw]: 分配一個輸入設備 */ 4 input_subsys_dev = input_allocate_device(); 5 input_subsys_dev->name = "input_subsys_dev"; 6 7 /* [cgw]: 設置支持的事件類型 */ 8 set_bit(EV_KEY, input_subsys_dev->evbit); 9 set_bit(EV_REP, input_subsys_dev->evbit); 10 11 set_bit(EV_LED, input_subsys_dev->evbit); 12 //set_bit(EV_SND, input_subsys_dev->evbit); 13 14 /* [cgw]: 設置支持的事件碼 */ 15 set_bit(KEY_L, input_subsys_dev->keybit); 16 set_bit(KEY_S, input_subsys_dev->keybit); 17 set_bit(KEY_ENTER, input_subsys_dev->keybit); 18 set_bit(KEY_LEFTSHIFT, input_subsys_dev->keybit); 19 20 set_bit(LED_MUTE, input_subsys_dev->ledbit); 21 //set_bit(SND_BELL, input_subsys_dev->sndbit); 22 23 /* [cgw]: 分配輸入設備的open方法 */ 24 input_subsys_dev->open = input_subsys_open; 25 /* [cgw]: 分配輸入設備的event方法,用戶在應用程序write()時 */ 26 input_subsys_dev->event = event_handler; 27 28 /* [cgw]: 注冊輸入設備 */ 29 input_register_device(input_subsys_dev); 30 31 ... 32 33 return 0; 34 }
1 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 2 { 3 ... 4 5 switch (type) { 6 ... 7 8 case EV_KEY: 9 10 if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) 11 return; 12 13 /* [cgw]: 收到連發按鍵的事件,返回 */ 14 if (value == 2) 15 break; 16 17 /* [cgw]: 這個函數的設置,用於上面!!test_bit(code, dev->key) == value判斷 18 * 是否為按下彈起操作 19 */ 20 change_bit(code, dev->key); 21 22 /* [cgw]: 如果當前操作為按下,並且連發功能使能,則設置連發的觸發時間 */ 23 if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) { 24 dev->repeat_key = code; 25 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); 26 } 27 28 break; 29 30 ... 31 32 } 33 34 .... 35 handle->handler->event(handle, type, code, value); 36 }
在input_event()中如果檢測到按鍵按下,一直到連發功能觸發,則定時器超時調用超時處理函數:
因為在注冊輸入設備時,就分配了定時器的超時處理函數input_repeat_key()
1 int input_register_device(struct input_dev *dev) 2 { 3 ... 4 /* 5 * If delay and period are pre-set by the driver, then autorepeating 6 * is handled by the driver itself and we don't do it in input.c. 7 */ 8 9 init_timer(&dev->timer); 10 if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { 11 dev->timer.data = (long) dev; 12 dev->timer.function = input_repeat_key; 13 dev->rep[REP_DELAY] = 250; 14 dev->rep[REP_PERIOD] = 33; 15 } 16 17 ... 18 }
在input_repeat_key()中
1 static void input_repeat_key(unsigned long data) 2 { 3 struct input_dev *dev = (void *) data; 4 5 /* [cgw]: 是否分配了連發的鍵值 */ 6 if (!test_bit(dev->repeat_key, dev->key)) 7 return; 8 9 /* [cgw]: 發送連發事件 */ 10 input_event(dev, EV_KEY, dev->repeat_key, 2); 11 input_sync(dev); 12 13 /* [cgw]: 設置連發間隔 */ 14 if (dev->rep[REP_PERIOD]) 15 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); 16 }
3.2 led控制事件處理過程
用戶在應用程序中操作write時
1 /* [cgw]: 發送LED控制事件 */ 2 write(fd, &leds_event, sizeof(struct input_event));
對應的是操作了evdev_write()
1 static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 2 { 3 ... 4 /* [cgw]: 收到來自用戶空間的事件 */ 5 if (evdev_event_from_user(buffer + retval, &event)) 6 return -EFAULT; 7 /* [cgw]: 調用input_event() */ 8 input_inject_event(&evdev->handle, event.type, event.code, event.value); 9 10 return retval; 11 }
input_event()
1 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 2 { 3 ... 4 5 switch (type) { 6 ... 7 8 case EV_LED: 9 10 if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) 11 return; 12 13 /* [cgw]: 這個函數用於上面!!test_bit(code, dev->led) == value是否為不同的LED狀態(亮,滅) */ 14 change_bit(code, dev->led); 15 16 /* [cgw]: 調用事件處理,這個事件處理需要驅動提供,做一些特別的處理 17 * 本例提供了 18 * static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value) 19 * 不影響調用evdev_event() 20 */ 21 if (dev->event) 22 dev->event(dev, type, code, value); 23 24 break; 25 26 ... 27 28 } 29 30 .... 31 handle->handler->event(handle, type, code, value); 32 }
event_handler()
1 static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value) 2 { 3 4 switch (type) { 5 case EV_REP: 6 return 0; 7 //break; 8 9 case EV_LED: 10 if (code == LED_MUTE) { 11 if (value == 0xAA) { 12 /* [cgw]: 點亮 */ 13 s3c2410_gpio_setpin(S3C2410_GPF4, 0); 14 } else if (value == 0xEE) { 15 /* [cgw]: 熄滅 */ 16 s3c2410_gpio_setpin(S3C2410_GPF4, 1); 17 } 18 19 return 0; 20 } 21 //break; 22 23 case EV_SND: 24 return 0; 25 //break; 26 } 27 28 return -1; 29 }
因此用戶空間發下來的事件,分兩個路徑處理
1.dev->event() 即:event_handler,由驅動程序提供
2.handler-event() 即:evdev_event(), 由evdev.c提供
http://xxxxxx/Linuxjc/1162992.html TechArticle