驅動開發之輸入子系統

驅動開發之輸入子系統:

輸入子系統
  事件處理層:drivers/input/evdev.c 給應用層提供統一的交互接口
  核心層:drivers/input/input.c 承上啓下(提供的接口會被設備驅動調用,調用後會訪問事件處理層)
  設備驅動層:本身實現,操做硬件linux

xxx子系統意義:數組

  內核實現的,爲了提升代碼的通用性app

vi drivers/input/input.c 框架

2401 static int __init input_init(void) 2402 { 2405 err = class_register(&input_class);//建立設備類
2411 err = input_proc_init();//在/proc目錄下建立input文件夾
2415 err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), //INPUT_MAJOR 爲13 說明全部的輸入子系統,使用的主設備號都是13 2416 INPUT_MAX_CHAR_DEVICES, "input"); 靜態註冊設備號 }


vi Documetation/devices.txtasync

488 13 char Input core 489 0 = /dev/input/js0 First joystick 遊戲手柄 490 1 = /dev/input/js1 Second joystick 491 ... 492 32 = /dev/input/mouse0 First mouse 鼠標 493 33 = /dev/input/mouse1 Second mouse 494 ... 495 63 = /dev/input/mice Unified mouse 通用鼠標 496 64 = /dev/input/event0 First event queue 除了手柄和鼠標以外的其餘輸入設備 497 65 = /dev/input/event1 Second event queue 498 ...

 

經過輸入子系統來實現驅動,基本操做過程:
一、給指定的結構體申請空間ide

1 struct input_dev *input_allocate_device(void)

給struct input_dev申請空間
二、基本配置
三、將申請好的結構體註冊到內核中函數

1 int input_register_device(struct input_dev *dev)

四、操做硬件atom

2067 int input_register_device(struct input_dev *dev) ------>

2138 input_attach_handler(dev, handler); ------->
991 error = handler->connect(handler, dev, id); --------> 進入事件處理層 1202 static struct input_handler evdev_handler = { 1203 .event = evdev_event, 1204 .events = evdev_events, 1205 .connect = evdev_connect, 1206 .disconnect = evdev_disconnect, 1207 .legacy_minors = true, 1208 .minor = EVDEV_MINOR_BASE, 1209 .name = "evdev", 1210 .id_table = evdev_ids, 1211 }; ---------->

1065 static const struct file_operations evdev_fops = { 1066 .owner = THIS_MODULE, 1067 .read = evdev_read, 1068 .write = evdev_write, 1069 .poll = evdev_poll, 1070 .open = evdev_open, 1071 .release = evdev_release, 1072 .unlocked_ioctl = evdev_ioctl, 1073 #ifdef CONFIG_COMPAT 1074 .compat_ioctl = evdev_ioctl_compat, 1075 #endif
1076 .fasync = evdev_fasync, 1077 .flush = evdev_flush, 1078 .llseek = no_llseek, 1079 }; 1113 static int evdev_connect(struct input_handler *handler, struct input_dev *d 1114 const struct input_device_id *id) 1115 { 1116 struct evdev *evdev; 1117 int minor; 1118 int dev_no; 1119 int error; 1120 
//獲取次設備號
1121 minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); 1128 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); 1140 dev_no = minor; 1142 if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS) 1143 dev_no -= EVDEV_MINOR_BASE; 1144 dev_set_name(&evdev->dev, "event%d", dev_no);//建立設備文件 
1161 cdev_init(&evdev->cdev, &evdev_fops); 1162 evdev->cdev.kobj.parent = &evdev->dev.kobj; 1163 error = cdev_add(&evdev->cdev, evdev->dev.devt, 1); } 

字符設備框架的基本搭建過程:spa

定義出各類接口函數; //已實現
struct file_operations fops = { //已實現 .owner = , .open = , .read = , ..... };
加載函數 {   設備號 = MKDEV(主設備號,次設備號);   register_chrdev_region(起始設備號,,);//已實現
  kzalloc();//申請空間,已實現
  cdev_init();//已實現
  cdev_add();//已實現
   class_create();//建立設備類 已實現
  device_create();//建立設備文件 已實現
}

 

總結:只要調用了input_register_chrdev()那麼字符設備框架以及函數接口均可以被建立。rest

vi -t input_event

421 void input_event(struct input_dev *dev, 422 unsigned int type, unsigned int code, int value) 423 { 424   unsigned long flags; 425 
426   if (is_event_supported(type, dev->evbit, EV_MAX)) {//保證爲真 427 
428       spin_lock_irqsave(&dev->event_lock, flags); 429       input_handle_event(dev, type, code, value); 430       spin_unlock_irqrestore(&dev->event_lock, flags); 431   } 432 }

 

1 void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value) //
調用喚醒接口

參數1:
參數2:事件的類型

143 #define EV_SYN 0x00
144 #define EV_KEY 0x01 
145 #define EV_REL 0x02
146 #define EV_ABS 0x03
147 #define EV_MSC 0x04
148 #define EV_SW 0x05
149 #define EV_LED 0x11
150 #define EV_SND 0x12
151 #define EV_REP 0x14
152 #define EV_FF 0x15
153 #define EV_PWR 0x16
154 #define EV_FF_STATUS 0x17
155 #define EV_MAX 0x1f

參數3:某種事件下,使用的具體值
參數4:狀態值,若是使用了按鍵,0表明按鍵按下狀態,1表明按鍵擡起狀態,

  2表明的只關心按下狀態內核源碼以下:

------->
368 disposition = input_get_disposition(dev, type, code, value); ------>
281 case EV_KEY: 282 if (is_event_supported(code, dev->keybit, KEY_MAX)) { 283 
284 /* auto-repeat bypasses state updates */ 
285 if (value == 2) { 286 disposition = INPUT_PASS_TO_HANDLERS; 287 break; 288 } 289 
290 if (!!test_bit(code, dev->key) != !!value) { 291 
292 __change_bit(code, dev->key); 293 disposition = INPUT_PASS_TO_HANDLERS ;//結果爲1
294 } 295 } 296 break;

  參數4能夠有邏輯真、邏輯假、能夠是2。內核源碼以下:

376 if (disposition & INPUT_PASS_TO_HANDLERS) { 377 struct input_value *v; 378 
379 if (disposition & INPUT_SLOT) {    //條件爲假
380 v = &dev->vals[dev->num_vals++]; 381 v->type = EV_ABS; 382 v->code = ABS_MT_SLOT; 383 v->value = dev->mt->slot; 384 } 385 
386 v = &dev->vals[dev->num_vals++]; 387 v->type = type;       //對結構體成員初始化,給別人用的
388 v->code = code; 389 v->value = value; 390 } 396 } else if (dev->num_vals >= dev->max_vals - 2) { 397 dev->vals[dev->num_vals++] = input_value_sync; 398 input_pass_values(dev, dev->vals, dev->num_vals); 399 dev->num_vals = 0; 400 } static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1}; type = EV_SYN code = SYN_REPORT value = 1; ------->

143 count = input_to_handler(handle, vals, count); ------>

116 if (handler->events) 117 handler->events(handle, vals, count); 若是條件成立則會調用evdev.c中的evdev_events函數 ----------->
1202 static struct input_handler evdev_handler = { 1203 .event = evdev_event, 1204 .events = evdev_events, 1205 .connect = evdev_connect, 1206 .disconnect = evdev_disconnect, 1207 .legacy_minors = true, 1208 .minor = EVDEV_MINOR_BASE, 1209 .name = "evdev", 1210 .id_table = evdev_ids, 1211 }; 進入事件處理層: 197 static void evdev_events(struct input_handle *handle, 198 const struct input_value *vals, unsigned int count) 199 { 211 if (client) 212 evdev_pass_values(client, vals, count, time_mono, time_real); } ------->

179 for (v = vals; v != vals + count; v++) { 180 event.type = v->type; //將type code value的值存放給struct input_event結構體
181 event.code = v->code; 182 event.value = v->value; 183 __pass_event(client, &event); 184 if (v->type == EV_SYN && v->code == SYN_REPORT) //最初type=EV_KEY code=KEY_2
185 wakeup = true; 186 } 187 
188 spin_unlock(&client->buffer_lock); 189 
190 if (wakeup) 191 wake_up_interruptible(&evdev->wait);//正常的需求下要調用喚醒接口。 //爲了保證調用喚醒接口,必需要保證type=EV_SYN code=SYN_REPORT   

 

如何保證?調用input_sync();

1 input_sync(fs4412_idev);//保證type=EV_SYN,code=SYN_REPORT,爲了調用喚醒接口

應用層中調用read(fd,&ev,sizeof(ev));實際上在驅動中會調用事件處理層定義的read函數

vi drivers/input/evdev.c:

vi drivers/input/evdev.c: 484 static ssize_t evdev_read(struct file *file, char __user *buffer, 485 size_t count, loff_t *ppos) { 514     if (input_event_to_user(buffer + read, &event)) 47         compat_event.time.tv_sec = event->time.tv_sec; 48     compat_event.time.tv_usec = event->time.tv_usec; 49     compat_event.type = event->type; 50     compat_event.code = event->code; 51     compat_event.value = event->value; 52 
53     if (copy_to_user(buffer, &compat_event,sizeof(struct input_event_compat))) 524     error = wait_event_interruptible(evdev->wait,client->packet_head != client->tail || !evdev->exist || client->revoked); }

 

爲何須要調用set_bit函數?
vi -t input_event

426 if (is_event_supported(type, dev->evbit, EV_MAX)) { //調用時input_event(EV_KEY,KEY_2,2);傳參後type=EV_KEY EV_MAX = 31

-------->
53 static inline int is_event_supported(unsigned int code,unsigned long *bm, unsigned int max) 55 { 56     return code <= max && test_bit(code, bm); 57 } //code=EV_KEY bm = dev->evbit 數組名 max = 31

103 static inline int test_bit(int nr, const volatile unsigned long *addr) 104 { 105     return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); 106 } //nr = EV_KEY addr = dev->evbit 1UL 表明的是unsigned long

#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) //<==> 1 / 32 = 0 addr[BIT_WORD(nr)] <==> addr[0] <==> dev->evbit[0];
 nr & (BITS_PER_LONG-1) <==> 1 & 31 結果爲1
 (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))) //理解爲dev->evbit[0] >> 1 //1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))) //理解爲:1 & (dev->evbit[0] >> 1) 保證這個結果爲真。暫時還保證不了,此時不知道dev->evbit[0]的值爲多少。 //解決方法:能夠將一個數值放到dev->evbit[0]中而且保證整個結果爲真.具體操做就須要調用set_bit()函數

vi -t set_bit

1 set_bit(EV_KEY,fs4412_idev->evbit)//基本配置
//調用時傳遞的是:set_bit(EV_KEY,fs4412_idev->evbit) 65 static inline void set_bit(int nr, volatile unsigned long *addr) 66 {    nr=EV_KEY=1 addr=fs4412_idev->evbit 67 unsigned long mask = BIT_MASK(nr); 68 unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); 69 unsigned long flags; 70 
71 _atomic_spin_lock_irqsave(p, flags); 72 *p |= mask; 73 _atomic_spin_unlock_irqrestore(p, flags); 74 } #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) 
//(1UL << (1 % 32)) <==> (1 << 1結果爲2)

#define BIT_WORD(nr) ((nr) / //BITS_PER_LONG) 《==》1 / 32 = 0 unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); = ((unsigned long *)addr) + 0; = ((unsignd long *)fs4412_idev->evbit); //*p |= mask; <==> *p = *p | mask; //*p = fs4412_idev->evbit[0] | mask; //*p = fs4412_idev->evbit[0] | 2; //fs4412_idev->evbit[0] = fs4412_idev->evbit[0] | 2; //不關心原有值是多少 //最終:1 & (dev->evbit[0] >> 1) <==> 1 & (2 >> 1) 爲真 //因此if (is_event_supported(type, dev->evbit, EV_MAX))才爲真。

參考代碼:

  1 #include <linux/module.h>
  2 #include <linux/platform_device.h>
  3 #include <linux/input.h>
  4 #include <linux/interrupt.h>
  5 #include <linux/irqreturn.h>
  6 #include <linux/sched.h>
  7 #include <linux/slab.h>
  8 
  9 struct input_dev *fs4412_idev;
 10 struct resource *res_key2;
 11 struct resource *res_key3;
 12 int flag2 = 0,flag3 = 0;//模擬按鍵的高低電平
 13 
 14 irqreturn_t fs4412_inputkey_handler(int irqno,void *id)
 15 {
 16 #if 0
 17     if(irqno == res_key2->start)
 18     {
 19         input_event(fs4412_idev,EV_KEY,KEY_2,2);//設置了type=,code=,value=,調用喚醒接口
 20         input_sync(fs4412_idev);//保證type=EV_SYN,code=SYN_REPORT,爲了調用喚醒接口
 21     }
 22     if(irqno == res_key3->start)
 23     {
 24         input_event(fs4412_idev,EV_KEY,KEY_3,2);
 25         input_sync(fs4412_idev);
 26     }
 27 #endif
 28     if(irqno == res_key2->start)
 29     {
 30         if(flag2 == 0)
 31         {
 32             input_event(fs4412_idev,EV_KEY,KEY_2,1);//設置了type=,code=,value=,調用喚醒接口
 33             input_sync(fs4412_idev);//保證type=EV_SYN,code=SYN_REPORT,爲了調用喚醒接口
 34             flag2 = 1;
 35         }
 36         if(flag2 == 1)
 37         {
 38             input_event(fs4412_idev,EV_KEY,KEY_2,0);//設置了type=,code=,value=,調用喚醒接口
 39             input_sync(fs4412_idev);//保證type=EV_SYN,code=SYN_REPORT,爲了調用喚醒接口
 40             flag2 = 0;
 41         }
 42     }
 43     if(irqno == res_key3->start)
 44     {
 45         if(flag3 == 0)
 46         {
 47             input_event(fs4412_idev,EV_KEY,KEY_3,1);
 48             input_sync(fs4412_idev);
 49             flag3 = 1;
 50         }
 51         if(flag3 == 1)
 52         {
 53             input_event(fs4412_idev,EV_KEY,KEY_3,0);
 54             input_sync(fs4412_idev);
 55             flag3 = 0;
 56         }
 57     }
 58 
 59     return IRQ_HANDLED;
 60 }
 61 
 62 struct of_device_id inputkey_match_tbl[] = {
 63     {
 64         .compatible = "fs4412,key",
 65     },
 66     {},
 67 };
 68 
 69 int fs4412_inputkey_probe(struct platform_device *pdev)
 70 {
 71     int ret;
 72     printk("match ok\n");
 73     fs4412_idev = input_allocate_device();//申請空間
 74 
 75     set_bit(EV_KEY,fs4412_idev->evbit);
 76     set_bit(KEY_2,fs4412_idev->keybit);
 77     set_bit(KEY_3,fs4412_idev->keybit);
 78 
 79     ret = input_register_device(fs4412_idev);
 80 
 81     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);
 82     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
 83     ret = request_irq(res_key2->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
 84     ret = request_irq(res_key3->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
 85     return 0;
 86 }
 87 
 88 int fs4412_inputkey_remove(struct platform_device *pdev)
 89 {
 90     free_irq(res_key3->start,NULL);
 91     free_irq(res_key2->start,NULL);
 92     
 93     input_unregister_device(fs4412_idev);
 94     kfree(fs4412_idev);
 95     return 0;
 96 }
 97 
 98 struct platform_driver pdrv = {
 99     .driver = {
100         .name = "inputkey",
101         .of_match_table = inputkey_match_tbl,
102     },
103 
104     .probe = fs4412_inputkey_probe,
105     .remove = fs4412_inputkey_remove,
106 };
107 
108 module_platform_driver(pdrv);
109 MODULE_LICENSE("GPL");
key.c
  1 #include <linux/module.h>
  2 #include <linux/platform_device.h>
  3 #include <linux/input.h>
  4 #include <linux/interrupt.h>
  5 #include <linux/irqreturn.h>
  6 #include <linux/sched.h>
  7 #include <linux/slab.h>
  8 
  9 struct input_dev *fs4412_idev;
 10 struct resource *res_key2;
 11 struct resource *res_key3;
 12 int flag2 = 0,flag3 = 0;//模擬按鍵的高低電平
 13 
 14 irqreturn_t fs4412_inputkey_handler(int irqno,void *id)
 15 {
 16 #if 0
 17     if(irqno == res_key2->start)
 18     {
 19         input_event(fs4412_idev,EV_KEY,KEY_2,2);//設置了type=,code=,value=,調用喚醒接口
 20         input_sync(fs4412_idev);//保證type=EV_SYN,code=SYN_REPORT,爲了調用喚醒接口
 21     }
 22     if(irqno == res_key3->start)
 23     {
 24         input_event(fs4412_idev,EV_KEY,KEY_3,2);
 25         input_sync(fs4412_idev);
 26     }
 27 #endif
 28     if(irqno == res_key2->start)
 29     {
 30         if(flag2 == 0)
 31         {
 32             input_event(fs4412_idev,EV_KEY,KEY_2,1);//設置了type=,code=,value=,調用喚醒接口
 33             input_sync(fs4412_idev);//保證type=EV_SYN,code=SYN_REPORT,爲了調用喚醒接口
 34             flag2 = 1;
 35         }
 36         if(flag2 == 1)
 37         {
 38             input_event(fs4412_idev,EV_KEY,KEY_2,0);//設置了type=,code=,value=,調用喚醒接口
 39             input_sync(fs4412_idev);//保證type=EV_SYN,code=SYN_REPORT,爲了調用喚醒接口
 40             flag2 = 0;
 41         }
 42     }
 43     if(irqno == res_key3->start)
 44     {
 45         if(flag3 == 0)
 46         {
 47             input_event(fs4412_idev,EV_KEY,KEY_3,1);
 48             input_sync(fs4412_idev);
 49             flag3 = 1;
 50         }
 51         if(flag3 == 1)
 52         {
 53             input_event(fs4412_idev,EV_KEY,KEY_3,0);
 54             input_sync(fs4412_idev);
 55             flag3 = 0;
 56         }
 57     }
 58 
 59     return IRQ_HANDLED;
 60 }
 61 
 62 struct of_device_id inputkey_match_tbl[] = {
 63     {
 64         .compatible = "fs4412,key",
 65     },
 66     {},
 67 };
 68 
 69 int fs4412_inputkey_probe(struct platform_device *pdev)
 70 {
 71     int ret;
 72     printk("match ok\n");
 73     fs4412_idev = input_allocate_device();//申請空間
 74 
 75     set_bit(EV_KEY,fs4412_idev->evbit);
 76     set_bit(KEY_2,fs4412_idev->keybit);
 77     set_bit(KEY_3,fs4412_idev->keybit);
 78 
 79     ret = input_register_device(fs4412_idev);
 80 
 81     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);
 82     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
 83     ret = request_irq(res_key2->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
 84     ret = request_irq(res_key3->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
 85     return 0;
 86 }
 87 
 88 int fs4412_inputkey_remove(struct platform_device *pdev)
 89 {
 90     free_irq(res_key3->start,NULL);
 91     free_irq(res_key2->start,NULL);
 92     
 93     input_unregister_device(fs4412_idev);
 94     kfree(fs4412_idev);
 95     return 0;
 96 }
 97 
 98 struct platform_driver pdrv = {
 99     .driver = {
100         .name = "inputkey",
101         .of_match_table = inputkey_match_tbl,
102     },
103 
104     .probe = fs4412_inputkey_probe,
105     .remove = fs4412_inputkey_remove,
106 };
107 
108 module_platform_driver(pdrv);
109 MODULE_LICENSE("GPL");
app.c
相關文章
相關標籤/搜索