首先gpio键盘的核心是:按下键盘后,gpio键盘往上报告键值,上层通过阻塞读,若没有键盘按下去,则读的进程睡眠。等有键盘按下后读的进程被唤醒。下面以东南大学自主研发的sep6200为例讲述gpio键盘驱动,及其上报过程。
首先从gpio_key_probe()处入手分析。
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
struct input_dev *input;
int i, error;
int wakeup = 0;
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
input = input_allocate_device();// 申请iput_dev结构,不用细究此函数
if (!ddata || !input) {
error = -ENOMEM;
goto fail1;
}
platform_set_drvdata(pdev, ddata);
input->name = pdev->name;
input->phys = "gpio_keys/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
ddata->input = input;
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
int irq = sep_gpio_keys[i].eint;
unsigned int type = button->type ?: EV_KEY;
sep0611_gpio_cfgpin(button->gpio, SEP0611_GPIO_IO); /* GPIO */
sep0611_gpio_dirpin(button->gpio, SEP0611_GPIO_IN);
bdata->input = input;
bdata->button = button;
setup_timer(&bdata->timer,
gpio_check_button, (unsigned long)bdata);
error = request_irq(irq, gpio_keys_isr,
IRQF_DISABLED, button->desc ? button->desc : "sep0611_gpiokeys", bdata);
if (error) {
pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
irq, error);
goto fail2;
}
if(button->active_low)
sep0611_gpio_setirq(button->gpio, DOWN_TRIG);
else
sep0611_gpio_setirq(button->gpio, UP_TRIG);
if (button->wakeup)
wakeup = 1;
input_set_capability(input, type, button->code);
}
sep0611_key_setup();
error = input_register_device(input); //注册输入设备,并且关联handler。
if (error) {
pr_err("gpio-keys: Unable to register input device, "
"error: %d\n", error);
goto fail2;
}
event_dev = input;
device_init_wakeup(&pdev->dev, wakeup);
return 0;
fail2:
while (--i >= 0) {
free_irq(sep_gpio_keys[i].eint,&ddata->data[i]);
if (pdata->buttons[i].debounce_interval)
del_timer_sync(&ddata->data[i].timer);
}
platform_set_drvdata(pdev, NULL);
fail1:
input_free_device(input);
kfree(ddata);
return error;
}
接下来看下中断处理函数:
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
struct gpio_keys_button *button = bdata->button;
int num_test;
key_dbg("The code is %d\n", button->code);
maskkey();
sep0611_gpio_clrirq(button->gpio);
if (button->debounce_interval)
mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(button->debounce_interval));
else
gpio_keys_report_event(bdata);
unmaskkey();
return IRQ_HANDLED;
}
继续跟踪gpio_keys_report_event()函数:
static void gpio_keys_report_event(struct gpio_button_data *bdata)
{
struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
int num, state;
num = sep0611_gpio_getpin(button->gpio);
state = num ^ button->active_low;// 确定state==1
input_event(input, type, button->code, state); // 上报键值
input_sync(input);// 同步,说明此次上报结束
if(state == KEY_PRESSED)
mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(KEY_PRESSED_TIMEDOUT));
}
继续看input_sync():
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
接下来:
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = 0;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) { //判断是否支持该事件类型
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
if (test_bit(code, input_abs_bypass)) {
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
接下来看 input_pass_event
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value); //遍历调用每个input设备对应的handler,必须之前先打开handle,通过evdev_open打开
rcu_read_unlock();
}
........................................................................................................................................................................................................
以上变为驱动上报键值过程,下面再来研究一下event层对于input层报告的这个键盘输入事件是如何来处理的.
........................................................................................................................................................................................................
drivers/input/evdev.c
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
struct timespec ts;
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
wake_up_interruptible(&evdev->wait); //唤醒睡眠在evdev->wait等待队列等待输入信息的进程(通知上层).通常在evdev_read里实现读进程睡眠,当有上层应用调用evdev_read进行读时,若没有按键,则读进程则会睡眠,若后来有按键则读进程在此处被唤醒。
}
当键盘驱动有按键发生时,上面所说的的 handle->handler->event(handle,type, code, value)就是运行evdev_event,但是必须等handle打开时才能调用到evdev_event,而handle的打开时通过evdev_open()实现的。
****************************************************************************************************************************************************************
上面介绍了键值的上报和存入buff的按键处理过程,下面说说上层用户空间如何获得此输入事件的
****************************************************************************************************************************************************************
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
}
上层通过fd=open(“/dev/input/event0",O_RDWR),经过系统调用然后会调用到evdev_open()函数,打开handle,为下面的调用 handle->handler->event(handle,type, code, value)做准。
上层应用然后通过read(fd,,&event,sizeof(struct input_event)),调用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;
struct input_event event;
int retval;
if (count < input_event_size())
return -EINVAL;
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); //阻塞,当用户层掉用read时,通过系统调用此函数,到此处后,进程会进入睡眠状态,即代码将停在此处,不会再执行下面的代码。当有键盘按下后,执行到 wake_up_interruptible(&evdev->wait)时,会唤醒此处的读进程,继续执行下面的代码。
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
return retval;
}
下面一节里以一个小程序实现睡眠为例介绍按键上报和阻塞读过程。
首先从gpio_key_probe()处入手分析。
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
struct input_dev *input;
int i, error;
int wakeup = 0;
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
input = input_allocate_device();// 申请iput_dev结构,不用细究此函数
if (!ddata || !input) {
error = -ENOMEM;
goto fail1;
}
platform_set_drvdata(pdev, ddata);
input->name = pdev->name;
input->phys = "gpio_keys/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
ddata->input = input;
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
int irq = sep_gpio_keys[i].eint;
unsigned int type = button->type ?: EV_KEY;
sep0611_gpio_cfgpin(button->gpio, SEP0611_GPIO_IO); /* GPIO */
sep0611_gpio_dirpin(button->gpio, SEP0611_GPIO_IN);
bdata->input = input;
bdata->button = button;
setup_timer(&bdata->timer,
gpio_check_button, (unsigned long)bdata);
error = request_irq(irq, gpio_keys_isr,
IRQF_DISABLED, button->desc ? button->desc : "sep0611_gpiokeys", bdata);
if (error) {
pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
irq, error);
goto fail2;
}
if(button->active_low)
sep0611_gpio_setirq(button->gpio, DOWN_TRIG);
else
sep0611_gpio_setirq(button->gpio, UP_TRIG);
if (button->wakeup)
wakeup = 1;
input_set_capability(input, type, button->code);
}
sep0611_key_setup();
error = input_register_device(input); //注册输入设备,并且关联handler。
if (error) {
pr_err("gpio-keys: Unable to register input device, "
"error: %d\n", error);
goto fail2;
}
event_dev = input;
device_init_wakeup(&pdev->dev, wakeup);
return 0;
fail2:
while (--i >= 0) {
free_irq(sep_gpio_keys[i].eint,&ddata->data[i]);
if (pdata->buttons[i].debounce_interval)
del_timer_sync(&ddata->data[i].timer);
}
platform_set_drvdata(pdev, NULL);
fail1:
input_free_device(input);
kfree(ddata);
return error;
}
接下来看下中断处理函数:
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
struct gpio_keys_button *button = bdata->button;
int num_test;
key_dbg("The code is %d\n", button->code);
maskkey();
sep0611_gpio_clrirq(button->gpio);
if (button->debounce_interval)
mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(button->debounce_interval));
else
gpio_keys_report_event(bdata);
unmaskkey();
return IRQ_HANDLED;
}
继续跟踪gpio_keys_report_event()函数:
static void gpio_keys_report_event(struct gpio_button_data *bdata)
{
struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
int num, state;
num = sep0611_gpio_getpin(button->gpio);
state = num ^ button->active_low;// 确定state==1
input_event(input, type, button->code, state); // 上报键值
input_sync(input);// 同步,说明此次上报结束
if(state == KEY_PRESSED)
mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(KEY_PRESSED_TIMEDOUT));
}
继续看input_sync():
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
接下来:
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = 0;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) { //判断是否支持该事件类型
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
if (test_bit(code, input_abs_bypass)) {
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
接下来看 input_pass_event
static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value); //遍历调用每个input设备对应的handler,必须之前先打开handle,通过evdev_open打开
rcu_read_unlock();
}
........................................................................................................................................................................................................
以上变为驱动上报键值过程,下面再来研究一下event层对于input层报告的这个键盘输入事件是如何来处理的.
........................................................................................................................................................................................................
drivers/input/evdev.c
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
struct timespec ts;
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
wake_up_interruptible(&evdev->wait); //唤醒睡眠在evdev->wait等待队列等待输入信息的进程(通知上层).通常在evdev_read里实现读进程睡眠,当有上层应用调用evdev_read进行读时,若没有按键,则读进程则会睡眠,若后来有按键则读进程在此处被唤醒。
}
当键盘驱动有按键发生时,上面所说的的 handle->handler->event(handle,type, code, value)就是运行evdev_event,但是必须等handle打开时才能调用到evdev_event,而handle的打开时通过evdev_open()实现的。
****************************************************************************************************************************************************************
上面介绍了键值的上报和存入buff的按键处理过程,下面说说上层用户空间如何获得此输入事件的
****************************************************************************************************************************************************************
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
}
上层通过fd=open(“/dev/input/event0",O_RDWR),经过系统调用然后会调用到evdev_open()函数,打开handle,为下面的调用 handle->handler->event(handle,type, code, value)做准。
上层应用然后通过read(fd,,&event,sizeof(struct input_event)),调用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;
struct input_event event;
int retval;
if (count < input_event_size())
return -EINVAL;
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); //阻塞,当用户层掉用read时,通过系统调用此函数,到此处后,进程会进入睡眠状态,即代码将停在此处,不会再执行下面的代码。当有键盘按下后,执行到 wake_up_interruptible(&evdev->wait)时,会唤醒此处的读进程,继续执行下面的代码。
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
return retval;
}
下面一节里以一个小程序实现睡眠为例介绍按键上报和阻塞读过程。