gpio键盘驱动

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

下面一节里以一个小程序实现睡眠为例介绍按键上报和阻塞读过程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值