input subsystem 函數解釋

怎麼寫符合輸入子系統的驅動程序??
1. 分配一個input_dev結構體
2. 設置
3. 註冊
4. 硬件相關的代碼,好比在中斷服務程序裏上報事件。
函數

參考/drivers/input/keyboard/gpio_keys.c測試

1. 定義入口和出口函數
 module_init(buttons_init);
 module_exit(buttons_exit);
 
入口函數 buttons_init


 struct input_dev {
  void *private;
spa

  const char *name;
  const char *phys;
  const char *uniq;
  struct input_id id;
code

  unsigned long evbit[NBITS(EV_MAX)];     // 表示能產生哪類事件
  unsigned long keybit[NBITS(KEY_MAX)];   // 表示能產生哪些按鍵
  unsigned long relbit[NBITS(REL_MAX)];   // 表示能產生哪些相對位移x,y,滾輪。
  unsigned long absbit[NBITS(ABS_MAX)];
  unsigned long mscbit[NBITS(MSC_MAX)];
  unsigned long ledbit[NBITS(LED_MAX)];
  unsigned long sndbit[NBITS(SND_MAX)];
  unsigned long ffbit[NBITS(FF_MAX)];
  unsigned long swbit[NBITS(SW_MAX)];
  ......
  
 }
進程

函數:buttons_init();
static int buttons_init(void)
{
 int i;
 
 /* 1. 分配一個input_dev結構體 */
 buttons_dev = input_allocate_device();;
事件

 /* 2. 設置 */
 /* 2.1 能產生哪類事件 */
 set_bit(EV_KEY, buttons_dev->evbit);
 //set_bit(EV_REP, buttons_dev->evbit);   //是否檢測重複按鍵??
 
 /* 2.2 能產生這類操做裏的哪些事件: L,S,ENTER,LEFTSHIT */
 set_bit(KEY_L, buttons_dev->keybit);
 set_bit(KEY_S, buttons_dev->keybit);
 set_bit(KEY_ENTER, buttons_dev->keybit);
 set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);
內存

 /* 3. 註冊 */
 input_register_device(buttons_dev);
 
 /* 4. 硬件相關的操做 */
 init_timer(&buttons_timer);                        // 初始化定時器,定時器是用來防抖動的。
 buttons_timer.function = buttons_timer_function;   // 定時時間處處理函數,在中斷裏面設置超時時間。
 add_timer(&buttons_timer);                         // 將buttons_timer加入進內核
 
    // 註冊四個中斷,判斷返回值。
 for (i = 0; i < 4; i++)
 {
  request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
 }
 
 return 0;
}
-----------------------------------------------------------------------------------------------------
get

2. 按鍵中斷處理函數 buttons_irq
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
 /* 10ms後啓動定時器 */
 // 將哪個按鍵用dev_id記錄下來,賦值給irq_pd
 irq_pd = (struct pin_desc *)dev_id;
 
 // 修改定時器,10ms後啓動檢測,防抖動,假設10ms到,則調用定時器時間到
    // 處理函數 buttons_timer_function(初始化時註冊的函數)
 mod_timer(&buttons_timer, jiffies+HZ/100);
 return IRQ_RETVAL(IRQ_HANDLED);
}
--------------------------------------------------------------------------------------------------------
input

3. 定時器時間處處理函數 buttons_timer_function
 3.1 讀引腳值 pinval = s3c2410_gpio_getpin(pindesc->pin);
 3.2 肯定是鬆開仍是按下if (pinval)= 0|1
 3.3
  上報事件:input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
  上報一個同步事件:input_sync(buttons_dev);
 
static void buttons_timer_function(unsigned long data)
{
 struct pin_desc * pindesc = irq_pd;
 unsigned int pinval;
原型

 if (!pindesc)
  return;
 
 pinval = s3c2410_gpio_getpin(pindesc->pin);

 if (pinval)
 {
  /* 鬆開 : 最後一個參數: 0-鬆開, 1-按下 */
  input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
  input_sync(buttons_dev);
 }
 else
 {
  /* 按下 */
  input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
  input_sync(buttons_dev);
 }
}
----------------------------------------------------------------------------------------------------
4. 出口函數
註銷時,有順序嗎??
static void buttons_exit(void)
{
 int i;
 for (i = 0; i < 4; i++)
 {
  free_irq(pins_desc[i].irq, &pins_desc[i]);  // 註銷中斷
 }

 del_timer(&buttons_timer);             // 除掉定時器
 input_unregister_device(buttons_dev);  // 註銷輸入設備
 input_free_device(buttons_dev);        // 釋放輸入設備的內存空間
}


=======================================================================================================
class類下的設備device由誰建立???

測試:
1.
hexdump /dev/event1  (open(/dev/event1), read(), )
           秒        微秒    類  code    value
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000

open以後read則調用evdev.read函數

static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
 struct evdev_client *client = file->private_data;
 struct evdev *evdev = client->evdev;
 int retval;

 if (count < evdev_event_size())
  return -EINVAL;

 // 非阻塞方式,讀不到數據馬上返回
 // client->head == client->tail   環形緩衝區:頭=尾,表示爲空
 if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
  return -EAGAIN;
  
 // 阻塞方式,讀不到數據進入睡眠
 retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist);
 if (retval)
  return retval;

 if (!evdev->exist)
  return -ENODEV;
 
 // client->head != client->tail,緩衝區不爲空,
 while (client->head != client->tail && retval + evdev_event_size() <= count) {

  struct input_event *event = (struct input_event *) client->buffer + client->tail;

  // 拷貝回用戶空間
  if (evdev_event_to_user(buffer + retval, event))
   return -EFAULT;

  client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
  retval += evdev_event_size();
 }
 return retval;
}

evdev_event_to_user函數:將const struct input_event結構體的event數據拷貝回用戶空間。
static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
{
 if (copy_to_user(buffer, event, sizeof(struct input_event)))
  return -EFAULT;
 return 0;
}

const struct input_event結構體原型:
// The event structure itself
struct input_event {
 struct timeval time;
 __u16 type;
 __u16 code;
 __s32 value;
};

struct timeval {
 time_t  tv_sec;  /* seconds */
 suseconds_t tv_usec; /* microseconds */
};

綜上獲得:
hexdump /dev/event1  (open(/dev/event1), read(), )
           秒        微秒    類  code    value
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000   // 按下按鍵
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000   // 同步事件
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000   // 鬆開按鍵
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000   // 同步事件

類    --> 按鍵類事件 0000 0001
          #define   EV_KEY 0x01
code  --> 按鍵碼 KEY_L,KEY_S...
          KEY_L = 38 = 0X0026
value --> 按下狀態  0000 0001
          鬆開狀態  0000 0000
   
2. 若是沒有啓動QT:
cat /dev/tty1
按:s2,s3,s4
就能夠獲得ls

或者:
exec 0</dev/tty1
而後能夠使用按鍵來輸入

3. 若是已經啓動了QT:
能夠點開記事本
而後按:s2,s3,s4

------------------------------------------------------------------------------
1. 殺死QT進程不行
2. 修改inittab
 #vi /etc/inittab
  2.1 修改rcS,
  ::sysinit:/etc/init.d/rCs
   # /etc/init.d/rcS
   不啓動QT
   註釋掉: #/bin/qpe.sh &
  
  2.2 而後從新啓動,reboot

3. keyboard.c 入口函數
  
 static const struct input_device_id kbd_ids[] = {
  {
   .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
   .evbit = { BIT(EV_KEY) },  // 只要支持按鍵類事件,均可以用keyboard的event函數。
  },

  {
   .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
   .evbit = { BIT(EV_SND) },
  },

  { },    /* Terminating entry */
 };
 
從新啓動後操做:
# insmod buttons.ko
# cat /dev/tty1
ls  (按鍵輸入)

#exec 0</dev/tty1   //標準輸入(keyboard輸入)改成tty1(從按鍵獲得的輸入)
ls  (按鍵輸入)

一個現象:按下按鍵不重複???
解決:
 註冊的時候加入重複事件:set_bit(EV_REP, buttons_dev->evbit);
 在函數input_event中有:
  case EV_KEY:
   if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
    return;

   if (value == 2)
    break;

   change_bit(code, dev->key);

   if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
    dev->repeat_key = code;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
   }
  break;

 //
 int input_register_device(struct input_dev *dev)
 {
  init_timer(&dev->timer);
  if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
   dev->timer.data = (long) dev;
   dev->timer.function = input_repeat_key;  // 重複按鍵調用的處理函數。
   dev->rep[REP_DELAY] = 250;
   dev->rep[REP_PERIOD] = 33;
  }
 ...

 }
 
input_repeat_key函數:
/drivers/input/input.c

static void input_repeat_key(unsigned long data)
{
 struct input_dev *dev = (void *) data;

 if (!test_bit(dev->repeat_key, dev->key))
  return;

 input_event(dev, EV_KEY, dev->repeat_key, 2);  // 重複上報事件,2:表示重複事件;1:按下;0:鬆開
 input_sync(dev);                               // 上報同步事件

 if (dev->rep[REP_PERIOD])
  mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));  // 再次修改定時器
}
  
  
# ps
  PID
  770   -sh

# ls -l /proc/770/fd # exec 0</dev/tty1    //將標準輸入改成/dev/tty1,從按鍵輸入。

相關文章
相關標籤/搜索