输入设备是典型的CDEV。
一般的工作机制时,按键或者触摸板在产生了数据后,发送一个中断,或者内核通过TIMER进行轮询,然后CPU通过SPI,I2C,USB等BUS,读取数据,并放入一个InputBuffer,CDEV的驱动,负责管理这个INBUF。
CDEV提供了文件操作接口,它是一个UADEV,所以用户程序可以通过read文件操作来读取INBUF的数据。
内核设计了INPUT子系统,由INPUTCORE处理通用服务。
INPUTCORE提供了服务API,如下。
struct input_dev* input_allocate_device(void);
void input_free_device(struct intput_dev* dev);
这里,涉及到一个重要的结构体,input_dev,它是一个输入设备的控制块。
struct input_dev{
const char* name;
const char* phys;
struct input_id id;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
ussigned long keybit[BITS_TO_LONGS[KEY_CNT]];
unsigned int keycodemax;
unsigned int keycodesize;
void* keycode;
...
};
INPUTDEV需要注册到内核中,所以内核提供了如下API
int input_register_device(struct input_dev * dev);
void input_unregister_device(struct input_dev* dev);
输入事件,用一个结构体来描述,input_event.
struct input_event{
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
INPUTCORE通过这个input_event来描述输入事件。并提供了API。
void input_event(struct input_dev* dev, unsigned int type, unsigned int code, int value);
void input_report_key(struct input_dev* dev, unsigned int code, int value);
void input_report_rel(struct input_dev* dev, unsigned int code, int value);
void input_report_abs(struct input_dev* dev, unsigned int code, int value);
void input_sync(struct input_dev* dev);
在drivers/input/keyboard/goio_keys.c中,基于INPUT架构,实现了一个通用的GPIO按键驱动。它同时也实现了platform_driver。
其中的关键代码是,IRQACTION这个Callback。
在其中,通过input_event和input_sync向INPUT子系统报告输入事件。
static irqreturn_t gpio_keys_irq_isr(int irq, void* dev_id)
{
struct gpio_button_data* bdata = dev_id;
const struct gpio_keys_button* button = bdata->button;
struct input_dev* input = bdata->input;
unsigned long flags;
...
if(!bdata->key_pressed){
...
input_event(input, EV_KEY, button->code, 1);
input_sync(input);
bdata->key_pressed = 1;
}
if(bdata->timer_debounce){
mod_timer(&bdata->timer, jiffies+msecs_to_jiffies(bdata->timer_debounce));
}
...
return IRQ_HANDLED;
}