Uinput

Posted by Vector Blog on January 25, 2018

uinput是Linux提供的一个可以在用户空间创建input设备的驱动程序。目前看到在Android 中蓝牙鼠标就是利用的 uinput 来发送key event .

kernel 中 uinput 的 init 代码如下: /drivers/input/misc/uinput.c

static int __init uinput_init(void)
{
    return misc_register(&uinput_misc);
}
 
module_init(uinput_init);

uinput_init函数中,调用misc_register创建了一个混杂设备,所谓混杂设备本质上主设备号为10的一个字符设备,uinput_misc定义如下

static struct miscdevice uinput_misc = {
    .fops       = &uinput_fops,
    .minor      = UINPUT_MINOR,
    .name       = UINPUT_NAME,
};
 
static const struct file_operations uinput_fops = {
    .owner      = THIS_MODULE,
    .open       = uinput_open,
    .release    = uinput_release,
    .read       = uinput_read,
    .write      = uinput_write,
    .poll       = uinput_poll,
    .unlocked_ioctl = uinput_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = uinput_compat_ioctl,
#endif
    .llseek     = no_llseek,
};

先以蓝牙鼠标的代码为实例, 来看其实现流程 :

void bta_hh_co_open(UINT8 dev_handle, UINT8 sub_class, tBTA_HH_ATTR_MASK attr_mask,
                    UINT8 app_id)
{
    ......
    // 打开 ‘ /dev/uhid ’ 节点
    p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC); 
    //创建线程监听uinput driver 上传的事件, 其中会读取上面获取到的文件句柄
    p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, p_dev); 
}

void bta_hh_co_send_hid_info(btif_hh_device_t *p_dev, char *dev_name, UINT16 vendor_id,
                             UINT16 product_id, UINT16 version, UINT8 ctry_code,
                             int dscp_len, UINT8 *p_dscp)
{
    struct uhid_event ev;
    //创建 uinput device
    ev.type = UHID_CREATE;
    strncpy((char*)ev.u.create.name, dev_name, sizeof(ev.u.create.name) - 1);
    ev.u.create.bus = BUS_BLUETOOTH;
    ev.u.create.vendor = vendor_id;
    ev.u.create.product = product_id;
    ...
    result = uhid_write(p_dev->fd, &ev);

可以看到这里首先打开 /dev/uhid 节点, 然后写入一个 type 为 UHID_CREATE 的 uhid_event 来创建uinput 设备 。

先来看一下该节点的 open 函数:

static int uinput_open(struct inode *inode, struct file *file)
{
    struct uinput_device *newdev;
 
    newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
    mutex_init(&newdev->mutex);
    spin_lock_init(&newdev->requests_lock);
    init_waitqueue_head(&newdev->requests_waitq);
    init_waitqueue_head(&newdev->waitq);
    newdev->state = UIST_NEW_DEVICE;
 
    file->private_data = newdev;
    nonseekable_open(inode, file);
 
    return 0;
}