輸入子系統(tǒng)(input)框架解析(基于Linux3.4.2)

我們自己寫驅(qū)動(dòng)的流程一般是:

  1. 自己確定或由系統(tǒng)自動(dòng)分配主設(shè)備號(hào)榆骚;

  2. 建立fops結(jié)構(gòu)猪钮;

  3. 使用register_chrdev在初始化函數(shù)中進(jìn)行注冊(cè)蔗草;

  4. 定義入口函數(shù)MODULE_INIT()和出口函數(shù)MODULE_EXIT()兰英。

但這種我們自己寫的驅(qū)動(dòng)程序,只有自己可以調(diào)用窖认。因?yàn)檫@種驅(qū)動(dòng)不標(biāo)準(zhǔn),只有別人知道驅(qū)動(dòng)用法的情況下才能使用告希。當(dāng)我們使用QT等標(biāo)準(zhǔn)程序時(shí)扑浸,這類標(biāo)準(zhǔn)程序不能打開像我們這樣的野驅(qū)動(dòng),所以燕偶,我們應(yīng)該讓我們的驅(qū)動(dòng)程序融入“標(biāo)準(zhǔn)”中去喝噪。這個(gè)標(biāo)準(zhǔn)就是linux提供的輸入子系統(tǒng)框架。

1指么、Linux輸入子系統(tǒng)框架

下圖是input輸入子系統(tǒng)框架:

image-20210714215436333

輸入子系統(tǒng)由設(shè)備驅(qū)動(dòng)層(Device)酝惧、核心層(InputCore)榴鼎、事件處理層(EventHandler)三部份組成。一個(gè)輸入事件(如鼠標(biāo)移動(dòng)晚唇,鍵盤按鍵按下巫财,joystick的移動(dòng)等等)通過(guò)input driver -> Input core -> Event handler -> userspace 到達(dá)用戶空間傳給應(yīng)用程序。

  1. 設(shè)備驅(qū)動(dòng)程序?qū)?/strong>:主要實(shí)現(xiàn)對(duì)硬件設(shè)備的讀寫訪問(wèn)缺亮,中斷設(shè)置翁涤,并把硬件產(chǎn)生的事件轉(zhuǎn)換為核心層定義的規(guī)范提交給事件處理層。

  2. 核心層:為設(shè)備驅(qū)動(dòng)層提供了規(guī)范和接口萌踱。設(shè)備驅(qū)動(dòng)層只要關(guān)心如何驅(qū)動(dòng)硬件并獲得硬件數(shù)據(jù)(例如按下的按鍵數(shù)據(jù))葵礼,然后調(diào)用核心層提供的接口,核心層會(huì)自動(dòng)把數(shù)據(jù)提交給事件處理層并鸵。

  3. 事件處理層:用戶編程的接口(設(shè)備節(jié)點(diǎn))鸳粉,并處理驅(qū)動(dòng)層提交的數(shù)據(jù)處理。

image-20210712223400336
  • /dev/input目錄下顯示的是已經(jīng)注冊(cè)在內(nèi)核中的設(shè)備編程接口园担,用戶通過(guò)open這些設(shè)備文件來(lái)打開不同的輸入設(shè)備進(jìn)行硬件操作届谈。
  • 事件處理層為不同硬件類型提供了用戶訪問(wèn)及處理接口。例如當(dāng)我們打開設(shè)備/dev/input/mice時(shí)弯汰,會(huì)調(diào)用到事件處理層的Mouse Handler來(lái)處理輸入事件艰山,這也使得設(shè)備驅(qū)動(dòng)層無(wú)需關(guān)心設(shè)備文件的操作,因?yàn)镸ouse Handler已經(jīng)有了對(duì)應(yīng)事件處理的方法咏闪。
  • 輸入子系統(tǒng)由內(nèi)核代碼drivers/input/input.c構(gòu)成曙搬,它的存在屏蔽了用戶到設(shè)備驅(qū)動(dòng)的交互細(xì)節(jié),為設(shè)備驅(qū)動(dòng)層和事件處理層提供了相互通信的統(tǒng)一界面鸽嫂。

2纵装、核心層:driver/input/input.c

分析一個(gè)驅(qū)動(dòng)程序,首先看他的入口函數(shù)据某,即init初始化函數(shù):

static const struct file_operations input_fops = {
  .owner = THIS_MODULE,
  .open = input_open_file,
 };
 
 static int __init input_init(void)
 {
  int err;
 
  err = class_register(&input_class);
  if (err) {
  printk(KERN_ERR "input: unable to register input_dev class\n");
  return err;
  }
 
  err = input_proc_init();
  if (err)
  goto fail1;
 
  err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
  if (err) {
  printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
  goto fail2;
  }
 
  return 0;
 
  fail2: input_proc_exit();
  fail1: class_unregister(&input_class);
  return err;
 }
  • 在初始化函數(shù)中我們可以看出這只是執(zhí)行了一個(gè)普通的字符設(shè)備注冊(cè)過(guò)程橡娄,創(chuàng)建了一個(gè)input類,在該類下并沒(méi)有創(chuàng)建具體設(shè)備癣籽,其余沒(méi)有什么特別挽唉。在字符設(shè)備注冊(cè)時(shí)的操作函數(shù)集合input_fops中只有一個(gè)open函數(shù),直觀上看一個(gè)open函數(shù)并不能執(zhí)行read等操作。那我們分析一下這個(gè)open函數(shù)究竟做了些什么:
 static int input_open_file(struct inode *inode, struct file *file)
 {
  struct input_handler *handler = input_table[iminor(inode) >> 5];
  const struct file_operations *old_fops, *new_fops = NULL;
  int err;
 
  /* No load-on-demand here? */
  if (!handler || !(new_fops = fops_get(handler->fops)))
  return -ENODEV;
 
  /*
  * That's _really_ odd. Usually NULL ->open means "nothing special",
  * not "no device". Oh, well...
  */
  if (!new_fops->open) {
  fops_put(new_fops);
  return -ENODEV;
  }
  old_fops = file->f_op;
  file->f_op = new_fops;
 
  err = new_fops->open(inode, file);
 
  if (err) {
  fops_put(file->f_op);
  file->f_op = fops_get(old_fops);
  }
  fops_put(old_fops);
  return err;
 }
  1. 其中iminor(inode)函數(shù)調(diào)用了MINOR(inode->i_rdev)讀取子設(shè)備號(hào)筷狼,然后將子設(shè)備除以32瓶籽,找到新掛載的input驅(qū)動(dòng)的數(shù)組號(hào),然后放在input_handler驅(qū)動(dòng)處理函數(shù)handler中桑逝。因?yàn)檩斎胱酉到y(tǒng)支持的設(shè)備大類就那么幾項(xiàng)棘劣,每項(xiàng)支持最多32個(gè)設(shè)備,除32意味著可將在這32區(qū)段的設(shè)備都能準(zhǔn)確定位到自己對(duì)應(yīng)的大類上楞遏,這些設(shè)備都可以使用對(duì)應(yīng)大類的公共fops茬暇。
  1. 例如:輸入子系統(tǒng)的事件設(shè)備evdev首昔,次設(shè)備號(hào)起始位置為64,之后32個(gè)設(shè)備都屬于evdev設(shè)備糙俗,input_table[iminor(inode) >> 5]勒奇,次設(shè)備號(hào)64~95的設(shè)備都對(duì)應(yīng)input_table[2],這個(gè)數(shù)組位置指向的是evdev的handler巧骚,這個(gè)區(qū)段內(nèi)設(shè)備的fops赊颠,在這個(gè)函數(shù)中都會(huì)指向evdev設(shè)備共用的fops,這樣就不用驅(qū)動(dòng)編寫者自己編寫fops劈彪,直接使用該設(shè)備類型下別人寫出的fops即可竣蹦。
  1. 若handler有值,說(shuō)明掛載有這個(gè)驅(qū)動(dòng)沧奴。就將handler結(jié)構(gòu)體里的成員file_operations * fops賦到新的file_operations * new_fops里面痘括。
  1. 再將新的file_operations *new_fops賦到file-> file_operations *f_op里, 此時(shí)input子系統(tǒng)的file_operations就等于新掛載的input驅(qū)動(dòng)的file_operations結(jié)構(gòu)體,即實(shí)現(xiàn)一個(gè)偷天換日的效果滔吠。
  1. 然后調(diào)用新掛載的input驅(qū)動(dòng)的old_fops里面的成員.open函數(shù)纲菌,打開驅(qū)動(dòng)open函數(shù)
  • 為什么除以32:linux輸入子系統(tǒng),作為將輸入設(shè)備標(biāo)準(zhǔn)化處理的一種方式疮绷,使別人使用這類驅(qū)動(dòng)時(shí)不必關(guān)心驅(qū)動(dòng)細(xì)節(jié)即可使用翰舌。輸入設(shè)備分為好多類型,鍵盤類冬骚、鼠標(biāo)類椅贱、觸摸屏類等等,linux將輸入子系統(tǒng)設(shè)備的主設(shè)備號(hào)定為13唉韭,次設(shè)備號(hào)以32為間隔細(xì)分了幾大類夜涕,(例如事件設(shè)備evdev犯犁,屬于次設(shè)備號(hào)為64起始向后32個(gè)成員都屬于事件設(shè)備分段属愤,64-95這些設(shè)備都屬于事件設(shè)備),這些設(shè)備次設(shè)備號(hào)除以32結(jié)果都是2酸役,在調(diào)用驅(qū)動(dòng)操作時(shí)他們都可以使用事件設(shè)備提供的fops住诸,從而不用驅(qū)動(dòng)編寫者自己編寫fops,大大提高了驅(qū)動(dòng)易用性

  • 分析:struct input_handler *handler = input_table[iminor(inode) >> 5];

    • 將傳入的節(jié)點(diǎn)的次設(shè)備號(hào)除以32(右移5位)涣澡,并以其作為索引贱呐,將數(shù)組input_table[]中的某一項(xiàng)賦值給handle。

    • 創(chuàng)建文件操作結(jié)構(gòu)體new_fops入桂,并將其用剛剛的handle結(jié)構(gòu)體中的fops初始化奄薇,實(shí)現(xiàn)復(fù)制。

  • 那么存放各類輸入設(shè)備的input_handler結(jié)構(gòu)體數(shù)組input_table是由誰(shuí)構(gòu)造抗愁,怎么初始化的呢馁蒂?

    全局搜索發(fā)現(xiàn)其被input_register_handler調(diào)用:

 int input_register_handler(struct input_handler *handler)
         {
          struct input_dev *dev;
         
          INIT_LIST_HEAD(&handler->h_list);
          //判斷傳進(jìn)來(lái)的文件操作結(jié)合不是空的進(jìn)行進(jìn)一步操作
          if (handler->fops != NULL) {
         
          //如果分配的位置已經(jīng)有值不為空呵晚,說(shuō)明此位置已經(jīng)被占用,返回EBUSY
          if (input_table[handler->minor >> 5])
          return -EBUSY;
          // 將 handler 放入 input_table數(shù)組沫屡,數(shù)組序號(hào)為次設(shè)備號(hào)/32
          input_table[handler->minor >> 5] = handler;
          }
          // 將 handler 放入 input_handler_list 鏈表
          list_add_tail(&handler->node, &input_handler_list);
         
          // 取出 input_dev_list 鏈表中的每一個(gè)dev與該handler進(jìn)行比對(duì)
          list_for_each_entry(dev, &input_dev_list, node)
          input_attach_handler(dev, handler);
         
          input_wakeup_procfs_readers();
          return 0;
         }
  • 全局搜索input_register_handler饵隙,發(fā)現(xiàn)evdev.c joydev.c mousedev.c等等都是通過(guò)input_register_handler向核心層注冊(cè)自己的結(jié)構(gòu):
----input_register_handler Matches (10 in 9 files) ----
     evbug_init in evbug.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) :  return input_register_handler(&evbug_handler);
     evdev_init in evdev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) :  return input_register_handler(&evdev_handler);
     input.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) line 1182 : int input_register_handler(struct input_handler *handler)
     input.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) line 1203 : EXPORT_SYMBOL(input_register_handler);
     input.h (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\include\linux) line 1130 : int input_register_handler(struct input_handler *);
     joydev_init in joydev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) :    return input_register_handler(&joydev_handler);
     kbd_init in keyboard.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\char) :  error = input_register_handler(&kbd_handler);
     mousedev_init in mousedev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) :    error = input_register_handler(&mousedev_handler);
     rfkill_handler_init in rfkill-input.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\net\rfkill) :     return input_register_handler(&rfkill_handler);
     tsdev_init in tsdev.c (F:\SourceInsightProj\JZ2440_2.6\linux-2.6.22.6\drivers\input) :  return input_register_handler(&tsdev_handler);
  • 分析其中一個(gè)evdev.c,其入口函數(shù)如下:
 static int __init evdev_init(void)
         {
          return input_register_handler(&evdev_handler);
         }
  • 看看evdev_handler結(jié)構(gòu)體的定義:
static struct input_handler evdev_handler = {
          .event =    evdev_event,
          //.connect:連接函數(shù)沮脖,將設(shè)備input_dev和某個(gè)input_handler建立連接
          .connect =  evdev_connect,
         
          .disconnect =   evdev_disconnect,

          //.fops:文件操作結(jié)構(gòu)體,其中evdev_fops函數(shù)就是自己的寫的操作函數(shù),然后賦到.fops中
          .fops =     &evdev_fops,
         
          //.minor:用來(lái)存放次設(shè)備號(hào)
          /*其中EVDEV_MINOR_BASE=64, 然后調(diào)用input_register_handler(&evdev_handler)后,由于EVDEV_MINOR_BASE/32=2,所以存到input_table[2]中,所以當(dāng)open打開這個(gè)input設(shè)備,就會(huì)進(jìn)入 input_open_file()函數(shù),執(zhí)行evdev_handler-> evdev_fops -> .open函數(shù)*/
          .minor =    EVDEV_MINOR_BASE,
         
          .name =     "evdev",
         
          /*.id_table : 表示能支持哪些輸入設(shè)備金矛,比如某個(gè)驅(qū)動(dòng)設(shè)備的input_dev->的id和某個(gè)input_handler的id_table相匹配,就會(huì)調(diào)用.connect連接函數(shù)*/
          .id_table = evdev_ids,
         };
         
         static const struct file_operations evdev_fops = {
          .owner      = THIS_MODULE,
          .read       = evdev_read,
          .write      = evdev_write,
          .poll       = evdev_poll,
          .open       = evdev_open,
          .release    = evdev_release,
          .unlocked_ioctl = evdev_ioctl,
         #ifdef CONFIG_COMPAT
          .compat_ioctl   = evdev_ioctl_compat,
         #endif
          .fasync     = evdev_fasync,
          .flush      = evdev_flush,
          .llseek     = no_llseek,
         };
  • input_register_device()函數(shù),如何創(chuàng)建驅(qū)動(dòng)設(shè)備
int input_register_device(struct input_dev *dev)   //*dev:要注冊(cè)的驅(qū)動(dòng)設(shè)備
 {
  ... ...
  list_add_tail(&dev->node, &input_dev_list);   //(1)放入鏈表中
  ... ...
  list_for_each_entry(handler, &input_handler_list, node)  //(2)
  input_attach_handler(dev, handler); 
  ... ...
 }

步驟如下:

(1)將要注冊(cè)的input_dev驅(qū)動(dòng)設(shè)備鏈入input_dev_list鏈表中勺届,其中input_handler_list在前面講過(guò),就是存放每個(gè)input_handle驅(qū)動(dòng)處理結(jié)構(gòu)體驶俊。
(2)然后list_for_each_entry()函數(shù)會(huì)將每個(gè)input_handle從鏈表中取出并放到handler中,然后再調(diào)用input_attach_handler()函數(shù)免姿,判斷每個(gè)input_handle的id_table能否支持當(dāng)前dev設(shè)備废睦,若兩者支持便進(jìn)行連接。

  • 回過(guò)頭來(lái)看注冊(cè)input_handlerinput_register_handler()函數(shù):
int input_register_handler(struct input_handler *handler)
     {
      struct input_dev *dev;
      int retval;
     
      retval = mutex_lock_interruptible(&input_mutex);
      if (retval)
      return retval;
     
      INIT_LIST_HEAD(&handler->h_list);
     
      if (handler->fops != NULL) {
      if (input_table[handler->minor >> 5]) {
      retval = -EBUSY;
      goto out;
      }
      input_table[handler->minor >> 5] = handler;
      }
     
      list_add_tail(&handler->node, &input_handler_list);
     
      list_for_each_entry(dev, &input_dev_list, node)//在設(shè)備列表中找出設(shè)備結(jié)構(gòu)體
      input_attach_handler(dev, handler);//設(shè)備結(jié)構(gòu)體中的id與handler中的id進(jìn)行匹配
     
      input_wakeup_procfs_readers();
     
      out:
      mutex_unlock(&input_mutex);
      return retval;
     }

發(fā)現(xiàn):不管新添加input_dev還是input_handler,都會(huì)調(diào)用input_attach_handler()判斷兩者id是否匹配, 若兩者匹配便進(jìn)行連接

  • input_attach_handler()如何實(shí)現(xiàn)匹配兩者id
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
         {
         ... ...
         id = input_match_device(handler->id_table, dev);  //匹配兩者
         
         if (!id)                                     //若不匹配,return退出
         return -ENODEV; 
         
         error = handler->connect(handler, dev, id);  //調(diào)用input_handler->connect函數(shù)建立連接
         ... ...
         }

發(fā)現(xiàn):若兩者匹配成功养泡,就會(huì)自動(dòng)進(jìn)入input_handler 的connect函數(shù)建立連接

  • evdev.c(事件驅(qū)動(dòng)) 的evdev_handler->connect函數(shù)來(lái)分析是怎樣建立連接的

    • evdev_handler.connect函數(shù)是evdev_connect(),代碼如下:
 static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) 
         {
         ... ... 
         for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); //查找驅(qū)動(dòng)設(shè)備的子設(shè)備號(hào)
          if (minor == EVDEV_MINORS) {  // EVDEV_MINORS=32,所以該事件下的驅(qū)動(dòng)設(shè)備最多存32個(gè),
          printk(KERN_ERR "evdev: no more free evdev devices\n");
          return -ENFILE;                //沒(méi)找到驅(qū)動(dòng)設(shè)備
          }
          ... ...
          evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //分配一個(gè)input_handle全局結(jié)構(gòu)體(沒(méi)有r)
          ... ...
          evdev->handle.dev = dev;              //指向參數(shù)input_dev驅(qū)動(dòng)設(shè)備
         evdev->handle.name = evdev->name;
         evdev->handle.handler = handler;    //指向參數(shù) input_handler驅(qū)動(dòng)處理結(jié)構(gòu)體
         evdev->handle.private = evdev;
         sprintf(evdev->name, "event%d", minor);    //(1)保存驅(qū)動(dòng)設(shè)備名字, event%d
         ... ...
         devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),  //(2) 將主設(shè)備號(hào)和次設(shè)備號(hào)轉(zhuǎn)換成dev_t類型
         cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name); 
          // (3)在input類下創(chuàng)建驅(qū)動(dòng)設(shè)備
         ... ...
         error = input_register_handle(&evdev->handle); //(4)注冊(cè)這個(gè)input_handle結(jié)構(gòu)體        
         ... ...
         }
  • 是在保存驅(qū)動(dòng)設(shè)備名字,名為event%d, 因?yàn)闆](méi)有設(shè)置子設(shè)備號(hào)嗜湃,默認(rèn)從小到大排列,其中event0是表示這個(gè)input子系統(tǒng),所以這個(gè)鍵盤驅(qū)動(dòng)名字就是event1
  • 是在保存驅(qū)動(dòng)設(shè)備的主次設(shè)備號(hào),其中主設(shè)備號(hào)INPUT_MAJOR=13,因?yàn)?code>EVDEV_MINOR_BASE=64,所以此設(shè)備號(hào)=64+驅(qū)動(dòng)程序本事子設(shè)備號(hào)
  • 在之前在2小結(jié)里就分析了input_class類結(jié)構(gòu),會(huì)在/sys/class/input類下創(chuàng)建驅(qū)動(dòng)設(shè)備event%d
  • 最終會(huì)進(jìn)入input_register_handle()函數(shù)來(lái)注冊(cè),代碼在下面
int input_register_handle(struct input_handle *handle)
         {
          struct input_handler *handler = handle->handler; //handler= input_handler驅(qū)動(dòng)處理結(jié)構(gòu)體 
         
          list_add_tail(&handle->d_node, &handle->dev->h_list); //(1)
          list_add_tail(&handle->h_node, &handler->h_list);    // (2)
         
          if (handler->start)
          handler->start(handle);
          return 0;
         }
  • 因?yàn)?code>handle->dev指向input_dev驅(qū)動(dòng)設(shè)備,所以就是將handle->d_node放入到input_dev驅(qū)動(dòng)設(shè)備的h_list鏈表中澜掩,即input_dev驅(qū)動(dòng)設(shè)備的h_list鏈表就指向handle->d_node购披。

  • 同樣, input_handler驅(qū)動(dòng)處理結(jié)構(gòu)體的h_list也指向了handle->h_node,兩者的.h_list都指向了同一個(gè)handle結(jié)構(gòu)體,然后通過(guò).h_list 來(lái)找到handle的成員.devhandler肩榕,便能找到對(duì)方建立連接了刚陡。

  • 建立了連接后,如何讀取evdev.c(事件驅(qū)動(dòng)) 的evdev_handler->.fops->.read函數(shù)呢株汉?

事件驅(qū)動(dòng)的.read函數(shù)是evdev_read()函數(shù),我們來(lái)分析下:

 static ssize_t evdev_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos)
 {
  ... ...
 /*判斷應(yīng)用層要讀取的數(shù)據(jù)是否正確*/
 if (count < evdev_event_size())
 return -EINVAL;
 
 /*在無(wú)數(shù)據(jù)且是非阻塞操作情況下打開,則立刻返回*/
  if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
 return -EAGAIN;
 
 /*否則筐乳,進(jìn)入睡眠狀態(tài)  */
  retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);
   ... ...           //上傳數(shù)據(jù)
 }
  • 若read函數(shù)進(jìn)入了休眠狀態(tài),又是誰(shuí)來(lái)喚醒

通過(guò)搜索evdev->wait這個(gè)等待隊(duì)列變量乔妈,找到evdev_event被誰(shuí)喚醒:

static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
 {
 ... ...
  wake_up_interruptible(&evdev->wait);   //有事件觸發(fā)蝙云,便喚醒等待中斷
 }

其中evdev_event()evdev.c(事件驅(qū)動(dòng))的evdev_handler->.event成員。當(dāng)有事件發(fā)生了(比如對(duì)于按鍵驅(qū)動(dòng)路召,當(dāng)有按鍵按下時(shí)),就會(huì)進(jìn)入.event函數(shù)中去處理事件勃刨。

  • 是誰(shuí)調(diào)用evdev_event()這個(gè).event事件驅(qū)動(dòng)函數(shù)?

應(yīng)該就是之前分析的input_dev那層調(diào)用的,我們來(lái)看看內(nèi)核 gpio_keys_isr()函數(shù)代碼例子就知道了(driver/input/keyboard/gpio_key.c)

static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
 {
  /*獲取按鍵值,賦到state里*/
  ... ...
 /*上報(bào)事件*/
 input_event(input, type, button->code, !!state);  
 input_sync(input);                        //同步信號(hào)通知,表示事件發(fā)送完畢
 }

顯然就是通過(guò)input_event()來(lái)調(diào)用.event事件函數(shù),我們來(lái)看看:

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
 struct input_handle *handle;
 ... ...
 
 /* 通過(guò)input_dev->h_list鏈表找到input_handle驅(qū)動(dòng)處理結(jié)構(gòu)體*/
 list_for_each_entry(handle, &dev->h_list, d_node)    
 if (handle->open)  //如果input_handle之前open 過(guò),那么這個(gè)就是我們的驅(qū)動(dòng)處理結(jié)構(gòu)體
  handle->handler->event(handle, type, code, value); //調(diào)用evdev_event()的.event事件函數(shù) 
 }

若之前驅(qū)動(dòng)input_dev和處理input_handler已經(jīng)通過(guò)input_handler.connect函數(shù)建立起了連接,那么就調(diào)用evdev_event().event事件函數(shù)股淡。

總結(jié):

  • 1.注冊(cè)輸入子系統(tǒng)身隐,進(jìn)入input_init():

    • 1)創(chuàng)建主設(shè)備號(hào)為13的input字符設(shè)備:register_chrdev(INPUT_MAJOR, "input", &input_fops);
  • 2.open打開驅(qū)動(dòng),進(jìn)入input_open_file():

    • 1)更新設(shè)備的file_oprations:file->f_op=fops_get(handler->fops);

    • 2)執(zhí)行file_oprations->open函數(shù):new_fops->open(inode, file);

  • 3.注冊(cè)input_handler唯灵,進(jìn)入input_register_handler():

    • 1)添加到input_table[]處理數(shù)組中:input_table[handler->minor >> 5] = handler;

    • 2)添加到input_handler_list鏈表中:list_add_tail(&handler->node, &input_handler_list);

    • 3)判斷input_dev的id贾铝,看是否有支持這個(gè)驅(qū)動(dòng)的設(shè)備:

      • 遍歷查找input_dev_list鏈表里所有input_dev:list_for_each_entry(dev, &input_dev_list, node)

      • 判斷兩者id,若兩者支持便進(jìn)行連接:input_attach_handler(dev, handler);

  • 4.注冊(cè)input_dev,進(jìn)入input_register_device():

    • 1)放在input_dev_list鏈表中:list_add_tail(&dev->node, &input_dev_list);

    • 2)判斷input_handler的id垢揩,是否有支持這個(gè)設(shè)備的驅(qū)動(dòng):

      • 遍歷查找input_handler_list鏈表里所有input_handler:list_for_each_entry(handler, &input_handler_list, node)

      • 判斷兩者id,若兩者支持便進(jìn)行連接:input_attach_handler(dev, handler);

  • 5.判斷input_handler和input_dev的id大脉,進(jìn)入input_attach_handler():

    • 1)匹配兩者id:input_match_device(handler->id_table, dev);

    • 2)匹配成功,則建立連接水孩。調(diào)用:input_handler ->connec(handler, dev, id);

  • 6.建立input_handler和input_dev的連接镰矿,進(jìn)入input_handler->connect():

    • 1)創(chuàng)建全局結(jié)構(gòu)體,通過(guò)input_handle結(jié)構(gòu)體連接雙方

      • 創(chuàng)建兩者連接的input_handle全局結(jié)構(gòu)體:evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

      • 連接input_dev->h_list:list_add_tail(&handle->d_node, &handle->dev->h_list);

      • 連接input_handle->h_list:list_add_tail(&handle->h_node, &handler->h_list);

  • 7.有事件發(fā)生時(shí)(比如按鍵中斷),在中斷函數(shù)中需要進(jìn)入input_event()上報(bào)事件:

    • 1)找到驅(qū)動(dòng)處理結(jié)構(gòu)體,然后執(zhí)行input_handler->event():

      • 通過(guò)input_dev ->h_list鏈表找到input_handle驅(qū)動(dòng)處理結(jié)構(gòu)體:list_for_each_entry(handle, &dev->h_list, d_node)

      • 如果input_handle之前open 過(guò),那么這個(gè)就是我們的驅(qū)動(dòng)處理結(jié)構(gòu)體(有可能一個(gè)驅(qū)動(dòng)設(shè)備在不同情況下有不同的驅(qū)動(dòng)處理方式):if (handle->open)

      • 調(diào)用evdev_event()的.event事件函數(shù):handle->handler->event(handle, type, code, value);

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末俘种,一起剝皮案震驚了整個(gè)濱河市秤标,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宙刘,老刑警劉巖苍姜,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異悬包,居然都是意外死亡衙猪,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門布近,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)垫释,“玉大人,你說(shuō)我怎么就攤上這事撑瞧】闷” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵预伺,是天一觀的道長(zhǎng)订咸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)酬诀,這世上最難降的妖魔是什么脏嚷? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮瞒御,結(jié)果婚禮上父叙,老公的妹妹穿的比我還像新娘。我一直安慰自己葵腹,他們只是感情好高每,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布屿岂。 她就那樣靜靜地躺著践宴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪爷怀。 梳的紋絲不亂的頭發(fā)上阻肩,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼烤惊。 笑死乔煞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柒室。 我是一名探鬼主播渡贾,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雄右!你這毒婦竟也來(lái)了空骚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤擂仍,失蹤者是張志新(化名)和其女友劉穎囤屹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逢渔,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肋坚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肃廓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片智厌。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盲赊,靈堂內(nèi)的尸體忽然破棺而出峦剔,到底是詐尸還是另有隱情,我是刑警寧澤角钩,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布吝沫,位于F島的核電站,受9級(jí)特大地震影響递礼,放射性物質(zhì)發(fā)生泄漏惨险。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一脊髓、第九天 我趴在偏房一處隱蔽的房頂上張望辫愉。 院中可真熱鬧,春花似錦将硝、人聲如沸恭朗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)痰腮。三九已至,卻和暖如春律罢,著一層夾襖步出監(jiān)牢的瞬間膀值,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沧踏,地道東北人歌逢。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像翘狱,于是被迫代替她去往敵國(guó)和親秘案。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容