input subsystem 笔记2

怎么写符合输入子系统的驱动程序??
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;

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

  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;
}
-----------------------------------------------------------------------------------------------------

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);
}
--------------------------------------------------------------------------------------------------------

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,从按键输入。

 

 

 

 

 

 

你可能感兴趣的:(timer,struct,buffer,input,keyboard,delay)