Linux内核(十四)Input 子系统详解 II —— 设备端 input 子系统注册 Input_register_device


本文章中与input子系统相关的结构体可参考input子系统结构体解析
input函数路径:drivers/input/input.c

input子系统初始化

// linux-3.18.24.x/drivers/input/input.c
static int __init input_init(void)
{
    int err;

    err = class_register(&input_class);            /* 注册类,放在sys/class下 */  
    if (err) {
        pr_err("unable to register input_dev class\n");
        return err;
    }

    err = input_proc_init();                    /* 在proc目录下建立相关目录  ---------下面详解 -----------*/  
    if (err)
        goto fail1;

    err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                     INPUT_MAX_CHAR_DEVICES, "input");        /* 注册字符设备编号,INPUT_MAJOR永远是13 */  
    if (err) {
        pr_err("unable to register char major %d", INPUT_MAJOR);
        goto fail2;
    }

    return 0;

 fail2: input_proc_exit();
 fail1: class_unregister(&input_class);
    return err;
}
subsys_initcall(input_init);

1、input_init用subsys_initcall修饰,因此启动会直接调用执行
2、所有的输入设备的主设备号都是13,input-core通过次设备号来进行输入设备进行分类

input_proc_init :在proc目录下建立相关目录

static int __init input_proc_init(void)
{
    struct proc_dir_entry *entry;

    /* 在/proc/bus目录下创建input */
    proc_bus_input_dir = proc_mkdir("bus/input", NULL);
    if (!proc_bus_input_dir)
        return -ENOMEM;
        
    /* 在/proc/bus/input目录下创建devices文件 */
    entry = proc_create("devices", 0, proc_bus_input_dir,
                &input_devices_fileops);
    if (!entry)
        goto fail1;

    /* 在/proc/bus/input目录下创建handlers文件 */
    entry = proc_create("handlers", 0, proc_bus_input_dir,
                &input_handlers_fileops);
    if (!entry)
        goto fail2;

    return 0;

 fail2: remove_proc_entry("devices", proc_bus_input_dir);
 fail1: remove_proc_entry("bus/input", NULL);
    return -ENOMEM;
}

初始化input设备内存

input_allocate_device:为新设备提供input_dev结构体内存空间

struct input_dev *input_allocate_device(void)
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_dev *dev;

    dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
    if (dev) {
        dev->dev.type = &input_dev_type;
        dev->dev.class = &input_class;
        device_initialize(&dev->dev);
        mutex_init(&dev->mutex);
        spin_lock_init(&dev->event_lock);
        init_timer(&dev->timer);
        INIT_LIST_HEAD(&dev->h_list);
        INIT_LIST_HEAD(&dev->node);

        dev_set_name(&dev->dev, "input%lu",
                 (unsigned long) atomic_inc_return(&input_no) - 1);

        __module_get(THIS_MODULE);
    }

    return dev;
}
EXPORT_SYMBOL(input_allocate_device);

input设备注册

int input_register_device(struct input_dev *dev)
{
    struct input_devres *devres = NULL;
    struct input_handler *handler;
    unsigned int packet_size;
    const char *path;
    int error;

    if (dev->devres_managed) {
        devres = devres_alloc(devm_input_device_unregister,
                      sizeof(struct input_devres), GFP_KERNEL);
        if (!devres)
            return -ENOMEM;

        devres->input = dev;
    }

    /* 每个input设备都会产生 EV_SYN/SYN_REPORT 事件。将evbit的第0位置1,表示支持所有的事件  */
    __set_bit(EV_SYN, dev->evbit);

    /* KEY_RESERVED 不能传到用户层 */
    __clear_bit(KEY_RESERVED, dev->keybit);

    /* 确保dev->evbit没有涉及到的位为0 */
    input_cleanse_bitmasks(dev);

    packet_size = input_estimate_events_per_packet(dev);
    if (dev->hint_events_per_packet < packet_size)
        dev->hint_events_per_packet = packet_size;

    dev->max_vals = dev->hint_events_per_packet + 2;
    dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
    if (!dev->vals) {
        error = -ENOMEM;
        goto err_devres_free;
    }

    /* 如果延时周期是程序预先设定的,那么是由驱动自动处理,主要是为了处理重复按键 */
    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;
    }

    /* 检查getkeycode函数有没有被定义。 没有定义使用默认的函数input_default_getkeycode */
    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;
        
    /* 检查setkeycode函数有没有被定义。 没有定义使用默认的函数input_default_setkeycode */
    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;

    /* 任何一个设备的注册都会经过这个函数,设备驱动模型中的核心函数 */
    error = device_add(&dev->dev);
    if (error)
        goto err_free_vals;

    /* 生成并返回设备的路径,后必需使用kfree()来释放结果 */
    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    pr_info("%s as %s\n",
        dev->name ? dev->name : "Unspecified device",
        path ? path : "N/A");
    kfree(path);

    /* 获取互斥锁 */
    error = mutex_lock_interruptible(&input_mutex);
    if (error)
        goto err_device_del;

    /* 将input_dev添加到input_dev_list链表 */
    list_add_tail(&dev->node, &input_dev_list);

    /* 遍历input_handler链表上,对链表中的每一个input_handler执行input_attach_handler函数*/
    list_for_each_entry(handler, &input_handler_list, node)        // -------- 下面详解 -------
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    /* 释放锁 */
    mutex_unlock(&input_mutex);

    if (dev->devres_managed) {
        dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
            __func__, dev_name(&dev->dev));
        devres_add(dev->dev.parent, devres);
    }
    return 0;

err_device_del:
    device_del(&dev->dev);
err_free_vals:
    kfree(dev->vals);
    dev->vals = NULL;
err_devres_free:
    devres_free(devres);
    return error;
}
EXPORT_SYMBOL(input_register_device);

1、设置input_devce结构体中的事件标志位
2、设置input_devce结构体中有关处理重复按键的成员
3、设置input_devce结构体中 获取和设置按键的函数
4、添加到设备驱动模型中
5、将input_dev添加到input_dev_list链表
6、遍历input_handler链表上,对链表中的每一个input_handler执行input_attach_handler函数

两条链表都是设置为全局变量

static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

list_for_each_entry是一个宏:遍历input_handler_list上的所有handler结构体

list_for_each_entry(handler, &input_handler_list, node)
    input_attach_handler(dev, handler);

// list_for_each_entry展开:
for (handler = list_first_entry(input_handler_list, &handler, node);    
     &handler->node != (input_handler_list);                    
     &handler = list_next_entry(&handler, node))

     input_attach_handler(dev, handler);                    // -------- 下面详解 -------

input_attach_handler:此函数用于匹配input_dev和input_handler

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int error;

    /* input 匹配设备 */
    id = input_match_device(handler, dev);            // -------- 下面详解 -------
    if (!id)
        return -ENODEV;

    /* 匹配成功,调用handler->connect函数 这个函数在事件处理器中定义 */
    error = handler->connect(handler, dev, id);
    if (error && error != -ENODEV)
        pr_err("failed to attach handler %s to device %s, error: %d\n",
               handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}

input_dev和input_handler匹配函数实现

static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;

    /* id_table 表示驱动支持的设备列表 */
    for (id = handler->id_table; id->flags || id->driver_info; id++) {

        // 匹配总线类型
        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
            if (id->bustype != dev->id.bustype)        // id_table[i]中支持的总线类型 与设备的标识中的总线类型比较
                continue;

        // 匹配制造商id
        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
            if (id->vendor != dev->id.vendor)
                continue;

        //匹配产品id
        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
            if (id->product != dev->id.product)
                continue;

        //匹配版本号
        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
            if (id->version != dev->id.version)
                continue;

        if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
            continue;

        if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
            continue;

        if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
            continue;

        if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
            continue;

        if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
            continue;

        if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
            continue;

        if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
            continue;

        if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
            continue;

        if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
            continue;

        // 调用匹配函数
        if (!handler->match || handler->match(handler, dev))
            return id;
    }

    return NULL;
}

int __bitmap_subset(const unsigned long bitmap1,const unsigned longbitmap2, int bits)
功能: 判断bitmap2是否是bitmap1的子集,是返回1,不是返回0

id_table[i]中支持相关的匹配项就匹配:成功就跳出;不成功匹配失败执行continue循环检测下一个


设备向input子系统注册实例

驱动里很多外设都向input子系统注册,这里介绍usb鼠标设备注册input子系统。结合上面介绍的函数了解在具体设备驱动中的使用。

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    //  USB 接口描述符被当成参数传入(USB 的一个接口表示一个功能)
    // 获取USB 设备描述
    struct usb_device *dev = interface_to_usbdev(intf);
    struct usb_host_interface *interface;
    struct usb_endpoint_descriptor *endpoint;
    struct usb_mouse *mouse;
    struct input_dev *input_dev;
    int pipe, maxp;
    int error = -ENOMEM;

    //  获取当前接口描述
    interface = intf->cur_altsetting;
    // 判断接口是否合法,根据HID规范,鼠标只有一个端点(且不包含端点0)
    if (interface->desc.bNumEndpoints != 1)
        return -ENODEV;
    //  获取端点0的描述符
    endpoint = &interface->endpoint[0].desc;
    //  判断端点类型是否合法,HID 规范,鼠标唯一的端点为中断端点,因为鼠标是中断控制
    if (!usb_endpoint_is_int_in(endpoint))
        return -ENODEV;
    //  创建中断管道(in),鼠标属于中断控制
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    /* 返回该端点能够传输的最大的包长度,鼠标的返回的最大数据包为4个字节。*/  
    //初始化URB的时候会用到这个长度,缓冲区的长度要依照maxp来决定,最大不能超过8
    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

    // 为mouse 申请内存
    //mouse结构的主要作用是赋值给usb_interface中的一个属性
    //以便于触发其它函数的时候通过usb_interface中的这个属性就可以知道相关信息
    //usb_interface中的这个属性是专门为了储存用户需要的数据的
    mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
    // 创建input设备
    input_dev = input_allocate_device();
    if (!mouse || !input_dev)
        goto fail1;
    //  为urb 传输申请内存,data 指向该地址空间,初始化urb 缓存区,第四个参数为dma相关
    mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);
    if (!mouse->data)
        goto fail1;
    //  申请urb 
    mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
    if (!mouse->irq)
        goto fail2;

    // 为mouse 的usbdev,dev 赋值
    mouse->usbdev = dev;
    mouse->dev = input_dev;
    //  为mouse 那么赋值制造商名称
    if (dev->manufacturer)
        strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));

    if (dev->product) {
        if (dev->manufacturer)
            strlcat(mouse->name, " ", sizeof(mouse->name));
        strlcat(mouse->name, dev->product, sizeof(mouse->name));
    }

    if (!strlen(mouse->name))
        snprintf(mouse->name, sizeof(mouse->name),
             "USB HIDBP Mouse %04x:%04x",
             le16_to_cpu(dev->descriptor.idVendor),
             le16_to_cpu(dev->descriptor.idProduct));

    usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
    strlcat(mouse->phys, "/input0", sizeof(mouse->phys));

    input_dev->name = mouse->name;
    input_dev->phys = mouse->phys;
    // 从dev 设备中获取总线类型,设备id,厂商id,版本号,设置父设备
    usb_to_input_id(dev, &input_dev->id);
    input_dev->dev.parent = &intf->dev;
    // 设置输入设备所支持的事件信息
    // 支持相对坐标和事件
    input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
    //  记录支持的按键值
    input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
        BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
    // 支持的相对坐标为鼠标移动坐标和滑轮坐标
    input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
    input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
        BIT_MASK(BTN_EXTRA);
    input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
    // 将mouse 传入input_dev,方便通过input_dev 获取全部的mouse 信息
    input_set_drvdata(input_dev, mouse);
    // 设置输入设备的open,close函数
    input_dev->open = usb_mouse_open;
    input_dev->close = usb_mouse_close;
    //  填充urb 模块,mouse 作为上下文被设置下去,另外usb_mouse_irq函数为回调
    // 当usb mouse 有事件产生时,回调被调用
    usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
             (maxp > 8 ? 8 : maxp),
             usb_mouse_irq, mouse, endpoint->bInterval);
    //  mouse->irq 就是urb,如下设置DMA传输相关,当flag为URB_NO_TRANSFER_DMA_MAP时
    // 表示优先使用transfer_dma,而不是transfer buffer
    mouse->irq->transfer_dma = mouse->data_dma;
    mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    //  注册input 设备
    error = input_register_device(mouse->dev);
    if (error)
        goto fail3;
    // 设置mouse 到 usb interface 中
    usb_set_intfdata(intf, mouse);
    return 0;

fail3:    
    usb_free_urb(mouse->irq);
fail2:    
    usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
fail1:    
    input_free_device(input_dev);
    kfree(mouse);
    return error;
}

usb鼠标检测函数

  • 获取接口; // 判断端点是否为中断模式
  • 申请一个input 设备并填充; ---- input_allocate_device
  • 设置事件和事件值;
  • 创建管道,设置大小,再申请缓存区;
  • 申请一个urb 用于与usb 设备通信;
  • 注册input 设备; ----- input_register_device
  • 设置接口数据;

设置事件和事件值有三种方式:
/* 第一种方法 /
__set_bit(EV_KEY, inputdev->evbit); /
设置产生按键事件 /
__set_bit(EV_REP, inputdev->evbit); /
重复事件 /
__set_bit(KEY_0, inputdev->keybit); /
设置产生哪些按键值 /
/
第二种方法 /
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |BIT_MASK(EV_REP);
keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |=BIT_MASK(KEY_0);
/
第三种方法 */
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bazinga bingo

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值