Re: [RFC 1/1] HID: User-space I/O driver support for HID subsystem

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi David,

On Fri, Mar 16, 2012 at 11:53 AM, David Herrmann
<dh.herrmann@xxxxxxxxxxxxxx> wrote:
> This driver allows to write I/O drivers in user-space and feed the input
> into the HID subsystem. It operates on the same level as USB-HID and
> Bluetooth-HID (HIDP). It does not provide support to write special HID
> device drivers but rather provides support for user-space I/O devices to
> feed their data into the kernel HID subsystem. The HID subsystem then
> loads the HID device drivers for the device and provides input-devices
> based on the user-space HID I/O device.
>
> This driver register a new char-device (/dev/uhid). A user-space process
> has to open this file for each device that it wants to provide to the
> kernel. It can then use write/read to communicate with the UHID driver.
> Both input and output data is sent with a uhid_event structure. The "type"
> field of the structure specifies what kind of event is sent. There is a
> file in Documentation/ explaining the ABI.
>
> Signed-off-by: David Herrmann <dh.herrmann@xxxxxxxxxxxxxx>
> ---
>  Documentation/hid/uhid.txt |   95 +++++++++
>  drivers/hid/Kconfig        |   21 ++
>  drivers/hid/Makefile       |    2 +-
>  drivers/hid/uhid.c         |  502 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/uhid.h       |   71 +++++++
>  5 files changed, 690 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/hid/uhid.txt
>  create mode 100644 drivers/hid/uhid.c
>  create mode 100644 include/linux/uhid.h
>
> diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt
> new file mode 100644
> index 0000000..67b138d
> --- /dev/null
> +++ b/Documentation/hid/uhid.txt
> @@ -0,0 +1,95 @@
> +      UHID - User-space I/O driver support for HID subsystem
> +     ========================================================
> +
> +The UHID driver provides an interface for user-space I/O drivers to feed their
> +data into the HID subsystem. The HID subsystem then parses the HID reports and
> +loads the corresponding HID device driver which then provides the parsed data
> +via input-devices to user-space.
> +
> +This allows user-space to operate on the same level as USB-HID, Bluetooth-HID
> +and similar. It does not provide a way to write HID device drivers, though! Use
> +HIDRAW for this purpose.
> +
> +UHID dynamically allocates the minor/major number, meaning that you should rely
> +on udev to create the UHID device node. Typically this is created as /dev/uhid.
> +
> +The UHID API
> +------------
> +
> +For each device that you want to register with the HID core, you need to open a
> +separate file-descriptor on /dev/uhid. All communication is done by read()'ing
> +or write()'ing "struct uhid_event" objects to the file. Non-blocking operations
> +via O_NONBLOCK are supported.
> +
> +struct uhid_event {
> +        __u32 type;
> +        ... payload ...
> +};
> +
> +write()
> +-------
> +write() allows you to modify the state of the device and feed input data into
> +the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and
> +UHID_INPUT.
> +
> +  UHID_CREATE:
> +  This creates the internal HID device. No I/O is possible until you send this
> +  event to the kernel. The payload is of type struct uhid_create_req and
> +  contains information about your device.
> +
> +  UHID_DESTROY:
> +  This destroys the internal HID device. No further I/O will be accepted. There
> +  may still be pending messages that you can receive with read() but no further
> +  UHID_INPUT events can be sent to the kernel.
> +  You can create a new device by sending UHID_CREATE again. There is no need to
> +  reopen the character device.
> +
> +  UHID_INPUT:
> +  You must send UHID_CREATE before sending input to the kernel! This event
> +  contains a data-payload. This is the raw data that you read from your device.
> +  The kernel will parse the HID reports and react on it.
> +
> +read()
> +------
> +read() will return a queued ouput report. These output reports can be of type
> +UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No
> +reaction is required to any of them but you should handle them according to your
> +needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
> +
> +  UHID_START:
> +  This is sent when the HID device is started. Consider this as an answer to
> +  UHID_CREATE. This is always the first event that is sent. No I/O is possible
> +  before you read this.
> +
> +  UHID_STOP:
> +  This is sent when the HID device is stopped. Consider this as an answer to
> +  UHID_DESTROY. No further I/O will be possible after receiving this.
> +  If the kernel HID device driver closes the device manually (that is, you
> +  didn't send UHID_DESTROY) then you should consider this device closed and send
> +  an UHID_DESTROY event. You may want to reregister your device, though.
> +
> +  UHID_OPEN:
> +  This is sent when the HID device is opened. That is, the data that the HID
> +  device provides is read by some other process. You may ignore this event but
> +  it is useful for power-management. As long as you haven't received this event
> +  there is actually no other process that reads your data so there is no need to
> +  send UHID_INPUT events to the kernel.
> +
> +  UHID_CLOSE:
> +  This is sent when there are no more processes which read the HID data. It is
> +  the counterpart of UHID_OPEN and you may as well ignore this event.
> +
> +  UHID_OUTPUT:
> +  This is sent if the HID device driver wants to send raw data to the I/O
> +  device. You should read the payload and forward it to the device. The payload
> +  is of type "struct uhid_data_req".
> +  This may be received even though you haven't received UHID_OPEN, yet.
> +
> +  UHID_OUTPUT_EV:
> +  Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This
> +  is called for force-feedback, LED or similar events which are received through
> +  an input device by the HID subsystem. You should convert this into raw reports
> +  and send them to your device similar to events of type UHID_OUTPUT.
> +
> +Document by:
> +  David Herrmann <dh.herrmann@xxxxxxxxxxxxxx>

What do you think about using ioctl() to handle creating, destroying
and configuring internal hid devices and leave read() and write() to
handle HID reports?

This way, at user-space, we wouldn't need to build uhid_event messages
for every HID report we get. We would just write() the HID report
right away.

> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 54cc92f..cabe771 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -55,6 +55,27 @@ config HIDRAW
>
>        If unsure, say Y.
>
> +config UHID
> +       tristate "User-space I/O driver support for HID subsystem"
> +       depends on HID
> +       default n
> +       ---help---
> +       Say Y here if you want to provide HID I/O drivers from user-space.
> +       This allows to write I/O drivers in user-space and feed the data from
> +       the device into the kernel. The kernel parses the HID reports, loads the
> +       corresponding HID device driver or provides input devices on top of your
> +       user-space device.
> +
> +       This driver cannot be used to parse HID-reports in user-space and write
> +       special HID-drivers. You should use HIDRAW for that.
> +       Instead, this driver allows to write the transport-layer driver in
> +       user-space like USB-HID and Bluetooth-HID do in kernel-space.
> +
> +       If unsure, say N.
> +
> +       To compile this driver as a module, choose M here: the
> +       module will be called uhid.
> +
>  source "drivers/hid/usbhid/Kconfig"
>
>  menu "Special HID drivers"
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index 22f1d16..cadb84f 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS
>  endif
>
>  obj-$(CONFIG_HID)              += hid.o
> +obj-$(CONFIG_UHID)             += uhid.o
>
>  hid-$(CONFIG_HIDRAW)           += hidraw.o
>
> @@ -88,4 +89,3 @@ obj-$(CONFIG_HID_WIIMOTE)     += hid-wiimote.o
>  obj-$(CONFIG_USB_HID)          += usbhid/
>  obj-$(CONFIG_USB_MOUSE)                += usbhid/
>  obj-$(CONFIG_USB_KBD)          += usbhid/
> -
> diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
> new file mode 100644
> index 0000000..1bb16a7
> --- /dev/null
> +++ b/drivers/hid/uhid.c
> @@ -0,0 +1,502 @@
> +/*
> + * User-space I/O driver support for HID subsystem
> + * Copyright (c) 2012 David Herrmann
> + */
> +
> +/*
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/fs.h>
> +#include <linux/hid.h>
> +#include <linux/input.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/poll.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/uhid.h>
> +#include <linux/wait.h>
> +
> +#define UHID_NAME      "uhid"
> +#define UHID_BUFSIZE   32
> +
> +enum uhid_state {
> +       UHID_NEW,
> +       UHID_RUNNING,
> +};
> +
> +struct uhid_device {
> +       struct mutex devlock;
> +       enum uhid_state state;
> +       struct device *parent;
> +
> +       __u8 *rd_data;
> +       uint rd_size;
> +
> +       struct hid_device *hid;
> +       struct uhid_event input_buf;
> +
> +       wait_queue_head_t waitq;
> +       spinlock_t qlock;
> +       struct uhid_event assemble;
> +       __u8 head;
> +       __u8 tail;
> +       struct uhid_event outq[UHID_BUFSIZE];
> +};
> +
> +static void uhid_queue(struct uhid_device *uhid, const struct uhid_event *ev)
> +{
> +       __u8 newhead;
> +
> +       newhead = (uhid->head + 1) % UHID_BUFSIZE;
> +
> +       if (newhead != uhid->tail) {
> +               memcpy(&uhid->outq[uhid->head], ev, sizeof(struct uhid_event));
> +               uhid->head = newhead;
> +               wake_up_interruptible(&uhid->waitq);
> +       } else {
> +               pr_warn("Output queue is full\n");
> +       }
> +}
> +
> +static int uhid_hid_start(struct hid_device *hid)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +       unsigned long flags;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return -ENODEV;
> +
> +       spin_lock_irqsave(&uhid->qlock, flags);
> +       memset(&uhid->assemble, 0, sizeof(uhid->assemble));
> +       uhid->assemble.type = UHID_START;
> +       uhid_queue(uhid, &uhid->assemble);
> +       spin_unlock_irqrestore(&uhid->qlock, flags);
> +
> +       return 0;
> +}
> +
> +static void uhid_hid_stop(struct hid_device *hid)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +       unsigned long flags;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return;
> +
> +       spin_lock_irqsave(&uhid->qlock, flags);
> +       memset(&uhid->assemble, 0, sizeof(uhid->assemble));
> +       uhid->assemble.type = UHID_STOP;
> +       uhid_queue(uhid, &uhid->assemble);
> +       spin_unlock_irqrestore(&uhid->qlock, flags);
> +
> +       hid->claimed = 0;
> +}
> +
> +static int uhid_hid_open(struct hid_device *hid)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +       unsigned long flags;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return -ENODEV;
> +
> +       spin_lock_irqsave(&uhid->qlock, flags);
> +       memset(&uhid->assemble, 0, sizeof(uhid->assemble));
> +       uhid->assemble.type = UHID_OPEN;
> +       uhid_queue(uhid, &uhid->assemble);
> +       spin_unlock_irqrestore(&uhid->qlock, flags);
> +
> +       return 0;
> +}
> +
> +static void uhid_hid_close(struct hid_device *hid)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +       unsigned long flags;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return;
> +
> +       spin_lock_irqsave(&uhid->qlock, flags);
> +       memset(&uhid->assemble, 0, sizeof(uhid->assemble));
> +       uhid->assemble.type = UHID_CLOSE;
> +       uhid_queue(uhid, &uhid->assemble);
> +       spin_unlock_irqrestore(&uhid->qlock, flags);
> +}
> +
> +static int uhid_hid_power(struct hid_device *hid, int level)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +
> +       /* TODO: Handle PM-hints. This isn't mandatory so we simply return 0
> +        * here.
> +        */
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return -ENODEV;
> +
> +       return 0;
> +}
> +
> +static int uhid_hid_input(struct input_dev *input, unsigned int type,
> +                               unsigned int code, int value)
> +{
> +       struct hid_device *hid = input_get_drvdata(input);
> +       struct uhid_device *uhid = hid->driver_data;
> +       unsigned long flags;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return -ENODEV;
> +
> +       spin_lock_irqsave(&uhid->qlock, flags);
> +       memset(&uhid->assemble, 0, sizeof(uhid->assemble));
> +
> +       uhid->assemble.type = UHID_OUTPUT_EV;
> +       uhid->assemble.u.data_ev.type = type;
> +       uhid->assemble.u.data_ev.code = code;
> +       uhid->assemble.u.data_ev.value = value;
> +
> +       uhid_queue(uhid, &uhid->assemble);
> +       spin_unlock_irqrestore(&uhid->qlock, flags);
> +
> +       return 0;
> +}
> +
> +static int uhid_hid_parse(struct hid_device *hid)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return -ENODEV;
> +
> +       return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
> +}
> +
> +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum,
> +                               __u8 *buf, size_t count, unsigned char rtype)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return -ENODEV;
> +
> +       /* TODO: we currently do not support this request. If we want this we
> +        * would need some kind of stream-locking but it isn't needed by the
> +        * main drivers, anyway.
> +        */
> +
> +       return -EOPNOTSUPP;
> +}
> +
> +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
> +                               unsigned char report_type)
> +{
> +       struct uhid_device *uhid = hid->driver_data;
> +       __u8 rtype;
> +       unsigned long flags;
> +
> +       switch (report_type) {
> +               case HID_FEATURE_REPORT:
> +                       rtype = UHID_FEATURE_REPORT;
> +                       break;
> +               case HID_OUTPUT_REPORT:
> +                       rtype = UHID_OUTPUT_REPORT;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +       }
> +
> +       if (count < 1 || count > UHID_DATA_MAX)
> +               return -EINVAL;
> +
> +       if (uhid->state != UHID_RUNNING)
> +               return -ENODEV;
> +
> +       spin_lock_irqsave(&uhid->qlock, flags);
> +       memset(&uhid->assemble, 0, sizeof(uhid->assemble));
> +
> +       uhid->assemble.type = UHID_OUTPUT;
> +       uhid->assemble.u.data.size = count;
> +       uhid->assemble.u.data.rtype = rtype;
> +       memcpy(uhid->assemble.u.data.data, buf, count);
> +
> +       uhid_queue(uhid, &uhid->assemble);
> +       spin_unlock_irqrestore(&uhid->qlock, flags);
> +
> +       return 0;
> +}
> +
> +static struct hid_ll_driver uhid_hid_driver = {
> +       .start = uhid_hid_start,
> +       .stop = uhid_hid_stop,
> +       .open = uhid_hid_open,
> +       .close = uhid_hid_close,
> +       .power = uhid_hid_power,
> +       .hidinput_input_event = uhid_hid_input,
> +       .parse = uhid_hid_parse,
> +};
> +
> +static int uhid_dev_create(struct uhid_device *uhid,
> +                               const struct uhid_event *ev)
> +{
> +       struct hid_device *hid;
> +       int ret;
> +
> +       ret = mutex_lock_interruptible(&uhid->devlock);
> +       if (ret)
> +               return ret;
> +
> +       if (uhid->state != UHID_NEW) {
> +               ret = -EALREADY;
> +               goto unlock;
> +       }
> +
> +       uhid->rd_size = ev->u.create.rd_size;
> +       uhid->rd_data = kzalloc(uhid->rd_size, GFP_KERNEL);
> +       if (!uhid->rd_data) {
> +               ret = -ENOMEM;
> +               goto unlock;
> +       }
> +
> +       if (copy_from_user(uhid->rd_data, ev->u.create.rd_data,
> +                               uhid->rd_size)) {
> +               ret = -EFAULT;
> +               goto err_free;
> +       }
> +
> +       hid = hid_allocate_device();
> +       if (IS_ERR(hid)) {
> +               ret = PTR_ERR(hid);
> +               goto err_free;
> +       }
> +
> +       strncpy(hid->name, ev->u.create.name, 128);
> +       hid->name[127] = 0;
> +       hid->ll_driver = &uhid_hid_driver;
> +       hid->hid_get_raw_report = uhid_hid_get_raw;
> +       hid->hid_output_raw_report = uhid_hid_output_raw;
> +       hid->bus = BUS_VIRTUAL;
> +       hid->country = ev->u.create.country;
> +       hid->vendor = ev->u.create.vendor;
> +       hid->product = ev->u.create.product;
> +       hid->version = ev->u.create.version;
> +       hid->phys[0] = 0;
> +       hid->uniq[0] = 0;
> +       hid->driver_data = uhid;
> +       hid->dev.parent = uhid->parent;
> +
> +       ret = hid_add_device(hid);
> +       if (ret) {
> +               pr_err("Cannot register HID device\n");
> +               goto err_hid;
> +       }
> +
> +       uhid->hid = hid;
> +       uhid->state = UHID_RUNNING;
> +       mutex_unlock(&uhid->devlock);
> +
> +       return 0;
> +
> +err_hid:
> +       hid_destroy_device(hid);
> +err_free:
> +       kfree(uhid->rd_data);
> +unlock:
> +       mutex_unlock(&uhid->devlock);
> +       return ret;
> +}
> +
> +static int uhid_dev_destroy(struct uhid_device *uhid)
> +{
> +       int ret;
> +
> +       ret = mutex_lock_interruptible(&uhid->devlock);
> +       if (ret)
> +               return ret;
> +
> +       if (uhid->state != UHID_RUNNING) {
> +               ret = -EINVAL;
> +               goto unlock;
> +       }
> +
> +       hid_destroy_device(uhid->hid);
> +       kfree(uhid->rd_data);
> +       uhid->state = UHID_NEW;
> +
> +unlock:
> +       mutex_unlock(&uhid->devlock);
> +       return ret;
> +}
> +
> +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
> +{
> +       int ret;
> +
> +       ret = mutex_lock_interruptible(&uhid->devlock);
> +       if (ret)
> +               return ret;
> +
> +       if (uhid->state != UHID_RUNNING) {
> +               ret = -EINVAL;
> +               goto unlock;
> +       }
> +
> +       hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.data.data,
> +                               ev->u.data.size, 0);
> +
> +unlock:
> +       mutex_unlock(&uhid->devlock);
> +       return ret;
> +}
> +
> +static int uhid_char_open(struct inode *inode, struct file *file)
> +{
> +       struct uhid_device *uhid;
> +
> +       uhid = kzalloc(sizeof(*uhid), GFP_KERNEL);
> +       if (!uhid)
> +               return -ENOMEM;
> +
> +       mutex_init(&uhid->devlock);
> +       spin_lock_init(&uhid->qlock);
> +       init_waitqueue_head(&uhid->waitq);
> +       uhid->state = UHID_NEW;
> +       uhid->parent = NULL;
> +
> +       file->private_data = uhid;
> +       nonseekable_open(inode, file);
> +
> +       return 0;
> +}
> +
> +static int uhid_char_release(struct inode *inode, struct file *file)
> +{
> +       struct uhid_device *uhid = file->private_data;
> +
> +       uhid_dev_destroy(uhid);
> +       kfree(uhid);
> +
> +       return 0;
> +}
> +
> +static ssize_t uhid_char_read(struct file *file, char __user *buffer,
> +                               size_t count, loff_t *ppos)
> +{
> +       struct uhid_device *uhid = file->private_data;
> +       int ret = 0;
> +       unsigned long flags;
> +       size_t len;
> +
> +       /* we need at least the "type" member of uhid_event */
> +       if (count < sizeof(__u32))
> +               return -EINVAL;
> +
> +try_again:
> +       if (file->f_flags & O_NONBLOCK) {
> +               if (uhid->head == uhid->tail)
> +                       return -EAGAIN;
> +       } else {
> +               ret = wait_event_interruptible(uhid->waitq,
> +                                               uhid->head != uhid->tail);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       spin_lock_irqsave(&uhid->qlock, flags);
> +
> +       if (uhid->head != uhid->tail) {
> +               len = min_t(size_t, count, sizeof(struct uhid_event));
> +               if (copy_to_user(buffer, &uhid->outq[uhid->tail], len))
> +                       ret = -EFAULT;
> +               else
> +                       uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE;
> +               spin_unlock_irqrestore(&uhid->qlock, flags);
> +       } else {
> +               spin_unlock_irqrestore(&uhid->qlock, flags);
> +               goto try_again;
> +       }
> +
> +       return ret ? ret : len;
> +}
> +
> +static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
> +                               size_t count, loff_t *ppos)
> +{
> +       struct uhid_device *uhid = file->private_data;
> +       int ret;
> +
> +       /* we need at least the "type" member of uhid_event */
> +       if (count < sizeof(__u32))
> +               return -EINVAL;
> +
> +       memset(&uhid->input_buf, 0, sizeof(uhid->input_buf));
> +       if (copy_from_user(&uhid->input_buf, buffer, count))
> +               return -EFAULT;
> +
> +       switch (uhid->input_buf.type) {
> +               case UHID_CREATE:
> +                       ret = uhid_dev_create(uhid, &uhid->input_buf);
> +                       break;
> +               case UHID_DESTROY:
> +                       ret = uhid_dev_destroy(uhid);
> +                       break;
> +               case UHID_INPUT:
> +                       ret = uhid_dev_input(uhid, &uhid->input_buf);
> +                       break;
> +               default:
> +                       ret = -EINVAL;
> +       }
> +
> +       return ret ? ret : count;
> +}
> +
> +static unsigned int uhid_char_poll(struct file *file, poll_table *wait)
> +{
> +       struct uhid_device *uhid = file->private_data;
> +
> +       poll_wait(file, &uhid->waitq, wait);
> +
> +       if (uhid->head != uhid->tail)
> +               return POLLIN | POLLRDNORM;
> +
> +       return 0;
> +}
> +
> +static const struct file_operations uhid_fops = {
> +       .owner          = THIS_MODULE,
> +       .open           = uhid_char_open,
> +       .release        = uhid_char_release,
> +       .read           = uhid_char_read,
> +       .write          = uhid_char_write,
> +       .poll           = uhid_char_poll,
> +       .llseek         = no_llseek,
> +};
> +
> +static struct miscdevice uhid_misc = {
> +       .fops           = &uhid_fops,
> +       .minor          = MISC_DYNAMIC_MINOR,
> +       .name           = UHID_NAME,
> +};
> +
> +static int __init uhid_init(void)
> +{
> +       return misc_register(&uhid_misc);
> +}
> +
> +static void __exit uhid_exit(void)
> +{
> +       misc_deregister(&uhid_misc);
> +}
> +
> +module_init(uhid_init);
> +module_exit(uhid_exit);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("David Herrmann <dh.herrmann@xxxxxxxxx>");
> +MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
> diff --git a/include/linux/uhid.h b/include/linux/uhid.h
> new file mode 100644
> index 0000000..1a7df0d
> --- /dev/null
> +++ b/include/linux/uhid.h
> @@ -0,0 +1,71 @@
> +#ifndef __UHID_H_
> +#define __UHID_H_
> +
> +/*
> + * User-space I/O driver support for HID subsystem
> + * Copyright (c) 2012 David Herrmann
> + */
> +
> +/*
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the Free
> + * Software Foundation; either version 2 of the License, or (at your option)
> + * any later version.
> + */
> +
> +#include <linux/input.h>
> +#include <linux/types.h>
> +
> +enum uhid_event_type {
> +       UHID_CREATE,
> +       UHID_DESTROY,
> +       UHID_START,
> +       UHID_STOP,
> +       UHID_OPEN,
> +       UHID_CLOSE,
> +       UHID_OUTPUT,
> +       UHID_OUTPUT_EV,
> +       UHID_INPUT,
> +};
> +
> +struct uhid_create_req {
> +       __u8 __user name[128];
> +       __u8 __user *rd_data;
> +       __u16 rd_size;
> +
> +       __u16 vendor;
> +       __u16 product;
> +       __u16 version;
> +       __u8 country;
> +};
> +
> +#define UHID_DATA_MAX 4096
> +
> +enum uhid_report_type {
> +       UHID_FEATURE_REPORT,
> +       UHID_OUTPUT_REPORT,
> +};
> +
> +struct uhid_data_req {
> +       __u8 data[UHID_DATA_MAX];
> +       __u16 size;
> +       __u8 rtype;
> +};
> +
> +struct uhid_data_ev_req {
> +       __u16 type;
> +       __u16 code;
> +       __s32 value;
> +};
> +
> +struct uhid_event {
> +       __u32 type;
> +
> +       union {
> +               struct uhid_create_req create;
> +               struct uhid_data_req data;
> +               struct input_event data_ev;
> +       } u;
> +};
> +
> +#endif /* __UHID_H_ */
> --
> 1.7.9.4

BR,

Andre
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux