11ccd7a2aSDavid Herrmann /* 21ccd7a2aSDavid Herrmann * User-space I/O driver support for HID subsystem 31ccd7a2aSDavid Herrmann * Copyright (c) 2012 David Herrmann 41ccd7a2aSDavid Herrmann */ 51ccd7a2aSDavid Herrmann 61ccd7a2aSDavid Herrmann /* 71ccd7a2aSDavid Herrmann * This program is free software; you can redistribute it and/or modify it 81ccd7a2aSDavid Herrmann * under the terms of the GNU General Public License as published by the Free 91ccd7a2aSDavid Herrmann * Software Foundation; either version 2 of the License, or (at your option) 101ccd7a2aSDavid Herrmann * any later version. 111ccd7a2aSDavid Herrmann */ 121ccd7a2aSDavid Herrmann 131ccd7a2aSDavid Herrmann #include <linux/atomic.h> 14befde022SDmitry Torokhov #include <linux/compat.h> 151ccd7a2aSDavid Herrmann #include <linux/device.h> 161ccd7a2aSDavid Herrmann #include <linux/fs.h> 171ccd7a2aSDavid Herrmann #include <linux/hid.h> 181ccd7a2aSDavid Herrmann #include <linux/input.h> 191ccd7a2aSDavid Herrmann #include <linux/miscdevice.h> 201ccd7a2aSDavid Herrmann #include <linux/module.h> 211ccd7a2aSDavid Herrmann #include <linux/mutex.h> 221ccd7a2aSDavid Herrmann #include <linux/poll.h> 231ccd7a2aSDavid Herrmann #include <linux/sched.h> 241ccd7a2aSDavid Herrmann #include <linux/spinlock.h> 251ccd7a2aSDavid Herrmann #include <linux/uhid.h> 261ccd7a2aSDavid Herrmann #include <linux/wait.h> 271ccd7a2aSDavid Herrmann 281ccd7a2aSDavid Herrmann #define UHID_NAME "uhid" 29ace3d861SDavid Herrmann #define UHID_BUFSIZE 32 30ace3d861SDavid Herrmann 31ace3d861SDavid Herrmann struct uhid_device { 32d937ae5fSDavid Herrmann struct mutex devlock; 33d365c6cfSDavid Herrmann bool running; 34d365c6cfSDavid Herrmann 35d365c6cfSDavid Herrmann __u8 *rd_data; 36d365c6cfSDavid Herrmann uint rd_size; 37d365c6cfSDavid Herrmann 38ace3d861SDavid Herrmann struct hid_device *hid; 396664ef72SDavid Herrmann struct uhid_event input_buf; 40ace3d861SDavid Herrmann 41ace3d861SDavid Herrmann wait_queue_head_t waitq; 42ace3d861SDavid Herrmann spinlock_t qlock; 43ace3d861SDavid Herrmann __u8 head; 44ace3d861SDavid Herrmann __u8 tail; 45ace3d861SDavid Herrmann struct uhid_event *outq[UHID_BUFSIZE]; 46fcfcf0deSDavid Herrmann 478cad5b01SDavid Herrmann /* blocking GET_REPORT support; state changes protected by qlock */ 48fcfcf0deSDavid Herrmann struct mutex report_lock; 49fcfcf0deSDavid Herrmann wait_queue_head_t report_wait; 505942b849SDavid Herrmann bool report_running; 518cad5b01SDavid Herrmann u32 report_id; 5211c22155SDavid Herrmann u32 report_type; 53fcfcf0deSDavid Herrmann struct uhid_event report_buf; 54ace3d861SDavid Herrmann }; 551ccd7a2aSDavid Herrmann 561ccd7a2aSDavid Herrmann static struct miscdevice uhid_misc; 571ccd7a2aSDavid Herrmann 58ace3d861SDavid Herrmann static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) 59ace3d861SDavid Herrmann { 60ace3d861SDavid Herrmann __u8 newhead; 61ace3d861SDavid Herrmann 62ace3d861SDavid Herrmann newhead = (uhid->head + 1) % UHID_BUFSIZE; 63ace3d861SDavid Herrmann 64ace3d861SDavid Herrmann if (newhead != uhid->tail) { 65ace3d861SDavid Herrmann uhid->outq[uhid->head] = ev; 66ace3d861SDavid Herrmann uhid->head = newhead; 67ace3d861SDavid Herrmann wake_up_interruptible(&uhid->waitq); 68ace3d861SDavid Herrmann } else { 69ace3d861SDavid Herrmann hid_warn(uhid->hid, "Output queue is full\n"); 70ace3d861SDavid Herrmann kfree(ev); 71ace3d861SDavid Herrmann } 72ace3d861SDavid Herrmann } 73ace3d861SDavid Herrmann 74ace3d861SDavid Herrmann static int uhid_queue_event(struct uhid_device *uhid, __u32 event) 75ace3d861SDavid Herrmann { 76ace3d861SDavid Herrmann unsigned long flags; 77ace3d861SDavid Herrmann struct uhid_event *ev; 78ace3d861SDavid Herrmann 79ace3d861SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL); 80ace3d861SDavid Herrmann if (!ev) 81ace3d861SDavid Herrmann return -ENOMEM; 82ace3d861SDavid Herrmann 83ace3d861SDavid Herrmann ev->type = event; 84ace3d861SDavid Herrmann 85ace3d861SDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags); 86ace3d861SDavid Herrmann uhid_queue(uhid, ev); 87ace3d861SDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags); 88ace3d861SDavid Herrmann 89ace3d861SDavid Herrmann return 0; 90ace3d861SDavid Herrmann } 91ace3d861SDavid Herrmann 92d365c6cfSDavid Herrmann static int uhid_hid_start(struct hid_device *hid) 93d365c6cfSDavid Herrmann { 94ec4b7deaSDavid Herrmann struct uhid_device *uhid = hid->driver_data; 95c2b2f16cSDavid Herrmann struct uhid_event *ev; 96c2b2f16cSDavid Herrmann unsigned long flags; 97ec4b7deaSDavid Herrmann 98c2b2f16cSDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL); 99c2b2f16cSDavid Herrmann if (!ev) 100c2b2f16cSDavid Herrmann return -ENOMEM; 101c2b2f16cSDavid Herrmann 102c2b2f16cSDavid Herrmann ev->type = UHID_START; 103c2b2f16cSDavid Herrmann 104c2b2f16cSDavid Herrmann if (hid->report_enum[HID_FEATURE_REPORT].numbered) 105c2b2f16cSDavid Herrmann ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS; 106c2b2f16cSDavid Herrmann if (hid->report_enum[HID_OUTPUT_REPORT].numbered) 107c2b2f16cSDavid Herrmann ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS; 108c2b2f16cSDavid Herrmann if (hid->report_enum[HID_INPUT_REPORT].numbered) 109c2b2f16cSDavid Herrmann ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS; 110c2b2f16cSDavid Herrmann 111c2b2f16cSDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags); 112c2b2f16cSDavid Herrmann uhid_queue(uhid, ev); 113c2b2f16cSDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags); 114c2b2f16cSDavid Herrmann 115c2b2f16cSDavid Herrmann return 0; 116d365c6cfSDavid Herrmann } 117d365c6cfSDavid Herrmann 118d365c6cfSDavid Herrmann static void uhid_hid_stop(struct hid_device *hid) 119d365c6cfSDavid Herrmann { 120ec4b7deaSDavid Herrmann struct uhid_device *uhid = hid->driver_data; 121ec4b7deaSDavid Herrmann 122ec4b7deaSDavid Herrmann hid->claimed = 0; 123ec4b7deaSDavid Herrmann uhid_queue_event(uhid, UHID_STOP); 124d365c6cfSDavid Herrmann } 125d365c6cfSDavid Herrmann 126d365c6cfSDavid Herrmann static int uhid_hid_open(struct hid_device *hid) 127d365c6cfSDavid Herrmann { 128e7191474SDavid Herrmann struct uhid_device *uhid = hid->driver_data; 129e7191474SDavid Herrmann 130e7191474SDavid Herrmann return uhid_queue_event(uhid, UHID_OPEN); 131d365c6cfSDavid Herrmann } 132d365c6cfSDavid Herrmann 133d365c6cfSDavid Herrmann static void uhid_hid_close(struct hid_device *hid) 134d365c6cfSDavid Herrmann { 135e7191474SDavid Herrmann struct uhid_device *uhid = hid->driver_data; 136e7191474SDavid Herrmann 137e7191474SDavid Herrmann uhid_queue_event(uhid, UHID_CLOSE); 138d365c6cfSDavid Herrmann } 139d365c6cfSDavid Herrmann 140d365c6cfSDavid Herrmann static int uhid_hid_parse(struct hid_device *hid) 141d365c6cfSDavid Herrmann { 142037c061bSDavid Herrmann struct uhid_device *uhid = hid->driver_data; 143037c061bSDavid Herrmann 144037c061bSDavid Herrmann return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); 145d365c6cfSDavid Herrmann } 146d365c6cfSDavid Herrmann 14711c22155SDavid Herrmann /* must be called with report_lock held */ 14811c22155SDavid Herrmann static int __uhid_report_queue_and_wait(struct uhid_device *uhid, 14911c22155SDavid Herrmann struct uhid_event *ev, 15011c22155SDavid Herrmann __u32 *report_id) 151289a7162SJiri Kosina { 152289a7162SJiri Kosina unsigned long flags; 153289a7162SJiri Kosina int ret; 154289a7162SJiri Kosina 155289a7162SJiri Kosina spin_lock_irqsave(&uhid->qlock, flags); 15611c22155SDavid Herrmann *report_id = ++uhid->report_id; 15711c22155SDavid Herrmann uhid->report_type = ev->type; 1585942b849SDavid Herrmann uhid->report_running = true; 159289a7162SJiri Kosina uhid_queue(uhid, ev); 160289a7162SJiri Kosina spin_unlock_irqrestore(&uhid->qlock, flags); 161289a7162SJiri Kosina 162289a7162SJiri Kosina ret = wait_event_interruptible_timeout(uhid->report_wait, 1635942b849SDavid Herrmann !uhid->report_running || !uhid->running, 1640e0d7520SDavid Herrmann 5 * HZ); 16511c22155SDavid Herrmann if (!ret || !uhid->running || uhid->report_running) 166289a7162SJiri Kosina ret = -EIO; 16711c22155SDavid Herrmann else if (ret < 0) 168289a7162SJiri Kosina ret = -ERESTARTSYS; 16911c22155SDavid Herrmann else 170289a7162SJiri Kosina ret = 0; 171289a7162SJiri Kosina 1725942b849SDavid Herrmann uhid->report_running = false; 173289a7162SJiri Kosina 17411c22155SDavid Herrmann return ret; 17511c22155SDavid Herrmann } 17611c22155SDavid Herrmann 17711c22155SDavid Herrmann static void uhid_report_wake_up(struct uhid_device *uhid, u32 id, 17811c22155SDavid Herrmann const struct uhid_event *ev) 17911c22155SDavid Herrmann { 18011c22155SDavid Herrmann unsigned long flags; 18111c22155SDavid Herrmann 18211c22155SDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags); 18311c22155SDavid Herrmann 18411c22155SDavid Herrmann /* id for old report; drop it silently */ 18511c22155SDavid Herrmann if (uhid->report_type != ev->type || uhid->report_id != id) 18611c22155SDavid Herrmann goto unlock; 18711c22155SDavid Herrmann if (!uhid->report_running) 18811c22155SDavid Herrmann goto unlock; 18911c22155SDavid Herrmann 19011c22155SDavid Herrmann memcpy(&uhid->report_buf, ev, sizeof(*ev)); 19111c22155SDavid Herrmann uhid->report_running = false; 19211c22155SDavid Herrmann wake_up_interruptible(&uhid->report_wait); 19311c22155SDavid Herrmann 19411c22155SDavid Herrmann unlock: 19511c22155SDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags); 19611c22155SDavid Herrmann } 19711c22155SDavid Herrmann 19811c22155SDavid Herrmann static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum, 19911c22155SDavid Herrmann u8 *buf, size_t count, u8 rtype) 20011c22155SDavid Herrmann { 20111c22155SDavid Herrmann struct uhid_device *uhid = hid->driver_data; 20211c22155SDavid Herrmann struct uhid_get_report_reply_req *req; 20311c22155SDavid Herrmann struct uhid_event *ev; 20411c22155SDavid Herrmann int ret; 20511c22155SDavid Herrmann 20611c22155SDavid Herrmann if (!uhid->running) 20711c22155SDavid Herrmann return -EIO; 20811c22155SDavid Herrmann 20911c22155SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL); 21011c22155SDavid Herrmann if (!ev) 21111c22155SDavid Herrmann return -ENOMEM; 21211c22155SDavid Herrmann 21311c22155SDavid Herrmann ev->type = UHID_GET_REPORT; 21411c22155SDavid Herrmann ev->u.get_report.rnum = rnum; 21511c22155SDavid Herrmann ev->u.get_report.rtype = rtype; 21611c22155SDavid Herrmann 21711c22155SDavid Herrmann ret = mutex_lock_interruptible(&uhid->report_lock); 21811c22155SDavid Herrmann if (ret) { 21911c22155SDavid Herrmann kfree(ev); 22011c22155SDavid Herrmann return ret; 22111c22155SDavid Herrmann } 22211c22155SDavid Herrmann 22311c22155SDavid Herrmann /* this _always_ takes ownership of @ev */ 22411c22155SDavid Herrmann ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id); 22511c22155SDavid Herrmann if (ret) 22611c22155SDavid Herrmann goto unlock; 22711c22155SDavid Herrmann 22811c22155SDavid Herrmann req = &uhid->report_buf.u.get_report_reply; 22911c22155SDavid Herrmann if (req->err) { 23011c22155SDavid Herrmann ret = -EIO; 23111c22155SDavid Herrmann } else { 23211c22155SDavid Herrmann ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX); 23311c22155SDavid Herrmann memcpy(buf, req->data, ret); 23411c22155SDavid Herrmann } 23511c22155SDavid Herrmann 236289a7162SJiri Kosina unlock: 237289a7162SJiri Kosina mutex_unlock(&uhid->report_lock); 23811c22155SDavid Herrmann return ret; 23911c22155SDavid Herrmann } 24011c22155SDavid Herrmann 24111c22155SDavid Herrmann static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum, 24211c22155SDavid Herrmann const u8 *buf, size_t count, u8 rtype) 24311c22155SDavid Herrmann { 24411c22155SDavid Herrmann struct uhid_device *uhid = hid->driver_data; 24511c22155SDavid Herrmann struct uhid_event *ev; 24611c22155SDavid Herrmann int ret; 24711c22155SDavid Herrmann 24811c22155SDavid Herrmann if (!uhid->running || count > UHID_DATA_MAX) 24911c22155SDavid Herrmann return -EIO; 25011c22155SDavid Herrmann 25111c22155SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL); 25211c22155SDavid Herrmann if (!ev) 25311c22155SDavid Herrmann return -ENOMEM; 25411c22155SDavid Herrmann 25511c22155SDavid Herrmann ev->type = UHID_SET_REPORT; 25611c22155SDavid Herrmann ev->u.set_report.rnum = rnum; 25711c22155SDavid Herrmann ev->u.set_report.rtype = rtype; 25811c22155SDavid Herrmann ev->u.set_report.size = count; 25911c22155SDavid Herrmann memcpy(ev->u.set_report.data, buf, count); 26011c22155SDavid Herrmann 26111c22155SDavid Herrmann ret = mutex_lock_interruptible(&uhid->report_lock); 26211c22155SDavid Herrmann if (ret) { 26311c22155SDavid Herrmann kfree(ev); 26411c22155SDavid Herrmann return ret; 26511c22155SDavid Herrmann } 26611c22155SDavid Herrmann 26711c22155SDavid Herrmann /* this _always_ takes ownership of @ev */ 26811c22155SDavid Herrmann ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id); 26911c22155SDavid Herrmann if (ret) 27011c22155SDavid Herrmann goto unlock; 27111c22155SDavid Herrmann 27211c22155SDavid Herrmann if (uhid->report_buf.u.set_report_reply.err) 27311c22155SDavid Herrmann ret = -EIO; 27411c22155SDavid Herrmann else 27511c22155SDavid Herrmann ret = count; 27611c22155SDavid Herrmann 27711c22155SDavid Herrmann unlock: 27811c22155SDavid Herrmann mutex_unlock(&uhid->report_lock); 27911c22155SDavid Herrmann return ret; 280289a7162SJiri Kosina } 281289a7162SJiri Kosina 2827c4003bcSDavid Herrmann static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum, 2837c4003bcSDavid Herrmann __u8 *buf, size_t len, unsigned char rtype, 2847c4003bcSDavid Herrmann int reqtype) 2857c4003bcSDavid Herrmann { 28611c22155SDavid Herrmann u8 u_rtype; 28711c22155SDavid Herrmann 28811c22155SDavid Herrmann switch (rtype) { 28911c22155SDavid Herrmann case HID_FEATURE_REPORT: 29011c22155SDavid Herrmann u_rtype = UHID_FEATURE_REPORT; 29111c22155SDavid Herrmann break; 29211c22155SDavid Herrmann case HID_OUTPUT_REPORT: 29311c22155SDavid Herrmann u_rtype = UHID_OUTPUT_REPORT; 29411c22155SDavid Herrmann break; 29511c22155SDavid Herrmann case HID_INPUT_REPORT: 29611c22155SDavid Herrmann u_rtype = UHID_INPUT_REPORT; 29711c22155SDavid Herrmann break; 29811c22155SDavid Herrmann default: 29911c22155SDavid Herrmann return -EINVAL; 30011c22155SDavid Herrmann } 30111c22155SDavid Herrmann 3027c4003bcSDavid Herrmann switch (reqtype) { 3037c4003bcSDavid Herrmann case HID_REQ_GET_REPORT: 30411c22155SDavid Herrmann return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype); 3057c4003bcSDavid Herrmann case HID_REQ_SET_REPORT: 30611c22155SDavid Herrmann return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype); 3077c4003bcSDavid Herrmann default: 3087c4003bcSDavid Herrmann return -EIO; 3097c4003bcSDavid Herrmann } 3107c4003bcSDavid Herrmann } 3117c4003bcSDavid Herrmann 312d365c6cfSDavid Herrmann static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, 313d365c6cfSDavid Herrmann unsigned char report_type) 314d365c6cfSDavid Herrmann { 3153b3baa82SDavid Herrmann struct uhid_device *uhid = hid->driver_data; 3163b3baa82SDavid Herrmann __u8 rtype; 3173b3baa82SDavid Herrmann unsigned long flags; 3183b3baa82SDavid Herrmann struct uhid_event *ev; 3193b3baa82SDavid Herrmann 3203b3baa82SDavid Herrmann switch (report_type) { 3213b3baa82SDavid Herrmann case HID_FEATURE_REPORT: 3223b3baa82SDavid Herrmann rtype = UHID_FEATURE_REPORT; 3233b3baa82SDavid Herrmann break; 3243b3baa82SDavid Herrmann case HID_OUTPUT_REPORT: 3253b3baa82SDavid Herrmann rtype = UHID_OUTPUT_REPORT; 3263b3baa82SDavid Herrmann break; 3273b3baa82SDavid Herrmann default: 3283b3baa82SDavid Herrmann return -EINVAL; 3293b3baa82SDavid Herrmann } 3303b3baa82SDavid Herrmann 3313b3baa82SDavid Herrmann if (count < 1 || count > UHID_DATA_MAX) 3323b3baa82SDavid Herrmann return -EINVAL; 3333b3baa82SDavid Herrmann 3343b3baa82SDavid Herrmann ev = kzalloc(sizeof(*ev), GFP_KERNEL); 3353b3baa82SDavid Herrmann if (!ev) 3363b3baa82SDavid Herrmann return -ENOMEM; 3373b3baa82SDavid Herrmann 3383b3baa82SDavid Herrmann ev->type = UHID_OUTPUT; 3393b3baa82SDavid Herrmann ev->u.output.size = count; 3403b3baa82SDavid Herrmann ev->u.output.rtype = rtype; 3413b3baa82SDavid Herrmann memcpy(ev->u.output.data, buf, count); 3423b3baa82SDavid Herrmann 3433b3baa82SDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags); 3443b3baa82SDavid Herrmann uhid_queue(uhid, ev); 3453b3baa82SDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags); 3463b3baa82SDavid Herrmann 3473b3baa82SDavid Herrmann return count; 348d365c6cfSDavid Herrmann } 349d365c6cfSDavid Herrmann 350596cfdd8SFrank Praznik static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, 351596cfdd8SFrank Praznik size_t count) 352596cfdd8SFrank Praznik { 35341abfb36SBenjamin Tissoires return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); 354596cfdd8SFrank Praznik } 355596cfdd8SFrank Praznik 356d365c6cfSDavid Herrmann static struct hid_ll_driver uhid_hid_driver = { 357d365c6cfSDavid Herrmann .start = uhid_hid_start, 358d365c6cfSDavid Herrmann .stop = uhid_hid_stop, 359d365c6cfSDavid Herrmann .open = uhid_hid_open, 360d365c6cfSDavid Herrmann .close = uhid_hid_close, 361d365c6cfSDavid Herrmann .parse = uhid_hid_parse, 3627c4003bcSDavid Herrmann .raw_request = uhid_hid_raw_request, 363596cfdd8SFrank Praznik .output_report = uhid_hid_output_report, 364d365c6cfSDavid Herrmann }; 365d365c6cfSDavid Herrmann 366befde022SDmitry Torokhov #ifdef CONFIG_COMPAT 367befde022SDmitry Torokhov 368befde022SDmitry Torokhov /* Apparently we haven't stepped on these rakes enough times yet. */ 369befde022SDmitry Torokhov struct uhid_create_req_compat { 370befde022SDmitry Torokhov __u8 name[128]; 371befde022SDmitry Torokhov __u8 phys[64]; 372befde022SDmitry Torokhov __u8 uniq[64]; 373befde022SDmitry Torokhov 374befde022SDmitry Torokhov compat_uptr_t rd_data; 375befde022SDmitry Torokhov __u16 rd_size; 376befde022SDmitry Torokhov 377befde022SDmitry Torokhov __u16 bus; 378befde022SDmitry Torokhov __u32 vendor; 379befde022SDmitry Torokhov __u32 product; 380befde022SDmitry Torokhov __u32 version; 381befde022SDmitry Torokhov __u32 country; 382befde022SDmitry Torokhov } __attribute__((__packed__)); 383befde022SDmitry Torokhov 384befde022SDmitry Torokhov static int uhid_event_from_user(const char __user *buffer, size_t len, 385befde022SDmitry Torokhov struct uhid_event *event) 386befde022SDmitry Torokhov { 387befde022SDmitry Torokhov if (is_compat_task()) { 388befde022SDmitry Torokhov u32 type; 389befde022SDmitry Torokhov 390befde022SDmitry Torokhov if (get_user(type, buffer)) 391befde022SDmitry Torokhov return -EFAULT; 392befde022SDmitry Torokhov 393befde022SDmitry Torokhov if (type == UHID_CREATE) { 394befde022SDmitry Torokhov /* 395befde022SDmitry Torokhov * This is our messed up request with compat pointer. 396befde022SDmitry Torokhov * It is largish (more than 256 bytes) so we better 397befde022SDmitry Torokhov * allocate it from the heap. 398befde022SDmitry Torokhov */ 399befde022SDmitry Torokhov struct uhid_create_req_compat *compat; 400befde022SDmitry Torokhov 40180897aa7SDavid Herrmann compat = kzalloc(sizeof(*compat), GFP_KERNEL); 402befde022SDmitry Torokhov if (!compat) 403befde022SDmitry Torokhov return -ENOMEM; 404befde022SDmitry Torokhov 405befde022SDmitry Torokhov buffer += sizeof(type); 406befde022SDmitry Torokhov len -= sizeof(type); 407befde022SDmitry Torokhov if (copy_from_user(compat, buffer, 408befde022SDmitry Torokhov min(len, sizeof(*compat)))) { 409befde022SDmitry Torokhov kfree(compat); 410befde022SDmitry Torokhov return -EFAULT; 411befde022SDmitry Torokhov } 412befde022SDmitry Torokhov 413befde022SDmitry Torokhov /* Shuffle the data over to proper structure */ 414befde022SDmitry Torokhov event->type = type; 415befde022SDmitry Torokhov 416befde022SDmitry Torokhov memcpy(event->u.create.name, compat->name, 417befde022SDmitry Torokhov sizeof(compat->name)); 418befde022SDmitry Torokhov memcpy(event->u.create.phys, compat->phys, 419befde022SDmitry Torokhov sizeof(compat->phys)); 420befde022SDmitry Torokhov memcpy(event->u.create.uniq, compat->uniq, 421befde022SDmitry Torokhov sizeof(compat->uniq)); 422befde022SDmitry Torokhov 423befde022SDmitry Torokhov event->u.create.rd_data = compat_ptr(compat->rd_data); 424befde022SDmitry Torokhov event->u.create.rd_size = compat->rd_size; 425befde022SDmitry Torokhov 426befde022SDmitry Torokhov event->u.create.bus = compat->bus; 427befde022SDmitry Torokhov event->u.create.vendor = compat->vendor; 428befde022SDmitry Torokhov event->u.create.product = compat->product; 429befde022SDmitry Torokhov event->u.create.version = compat->version; 430befde022SDmitry Torokhov event->u.create.country = compat->country; 431befde022SDmitry Torokhov 432befde022SDmitry Torokhov kfree(compat); 433befde022SDmitry Torokhov return 0; 434befde022SDmitry Torokhov } 435befde022SDmitry Torokhov /* All others can be copied directly */ 436befde022SDmitry Torokhov } 437befde022SDmitry Torokhov 438befde022SDmitry Torokhov if (copy_from_user(event, buffer, min(len, sizeof(*event)))) 439befde022SDmitry Torokhov return -EFAULT; 440befde022SDmitry Torokhov 441befde022SDmitry Torokhov return 0; 442befde022SDmitry Torokhov } 443befde022SDmitry Torokhov #else 444befde022SDmitry Torokhov static int uhid_event_from_user(const char __user *buffer, size_t len, 445befde022SDmitry Torokhov struct uhid_event *event) 446befde022SDmitry Torokhov { 447befde022SDmitry Torokhov if (copy_from_user(event, buffer, min(len, sizeof(*event)))) 448befde022SDmitry Torokhov return -EFAULT; 449befde022SDmitry Torokhov 450befde022SDmitry Torokhov return 0; 451befde022SDmitry Torokhov } 452befde022SDmitry Torokhov #endif 453befde022SDmitry Torokhov 4544522643aSPetri Gynther static int uhid_dev_create2(struct uhid_device *uhid, 4554522643aSPetri Gynther const struct uhid_event *ev) 4564522643aSPetri Gynther { 4574522643aSPetri Gynther struct hid_device *hid; 45825be7fe2SDavid Herrmann size_t rd_size, len; 45941c4a464SDavid Herrmann void *rd_data; 4604522643aSPetri Gynther int ret; 4614522643aSPetri Gynther 4624522643aSPetri Gynther if (uhid->running) 4634522643aSPetri Gynther return -EALREADY; 4644522643aSPetri Gynther 46541c4a464SDavid Herrmann rd_size = ev->u.create2.rd_size; 46641c4a464SDavid Herrmann if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE) 4674522643aSPetri Gynther return -EINVAL; 4684522643aSPetri Gynther 46941c4a464SDavid Herrmann rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL); 47041c4a464SDavid Herrmann if (!rd_data) 4714522643aSPetri Gynther return -ENOMEM; 4724522643aSPetri Gynther 47341c4a464SDavid Herrmann uhid->rd_size = rd_size; 47441c4a464SDavid Herrmann uhid->rd_data = rd_data; 47541c4a464SDavid Herrmann 4764522643aSPetri Gynther hid = hid_allocate_device(); 4774522643aSPetri Gynther if (IS_ERR(hid)) { 4784522643aSPetri Gynther ret = PTR_ERR(hid); 4794522643aSPetri Gynther goto err_free; 4804522643aSPetri Gynther } 4814522643aSPetri Gynther 48225be7fe2SDavid Herrmann len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1; 48325be7fe2SDavid Herrmann strncpy(hid->name, ev->u.create2.name, len); 48425be7fe2SDavid Herrmann len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1; 48525be7fe2SDavid Herrmann strncpy(hid->phys, ev->u.create2.phys, len); 48625be7fe2SDavid Herrmann len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1; 48725be7fe2SDavid Herrmann strncpy(hid->uniq, ev->u.create2.uniq, len); 4884522643aSPetri Gynther 4894522643aSPetri Gynther hid->ll_driver = &uhid_hid_driver; 4904522643aSPetri Gynther hid->bus = ev->u.create2.bus; 4914522643aSPetri Gynther hid->vendor = ev->u.create2.vendor; 4924522643aSPetri Gynther hid->product = ev->u.create2.product; 4934522643aSPetri Gynther hid->version = ev->u.create2.version; 4944522643aSPetri Gynther hid->country = ev->u.create2.country; 4954522643aSPetri Gynther hid->driver_data = uhid; 4964522643aSPetri Gynther hid->dev.parent = uhid_misc.this_device; 4974522643aSPetri Gynther 4984522643aSPetri Gynther uhid->hid = hid; 4994522643aSPetri Gynther uhid->running = true; 5004522643aSPetri Gynther 5014522643aSPetri Gynther ret = hid_add_device(hid); 5024522643aSPetri Gynther if (ret) { 5034522643aSPetri Gynther hid_err(hid, "Cannot register HID device\n"); 5044522643aSPetri Gynther goto err_hid; 5054522643aSPetri Gynther } 5064522643aSPetri Gynther 5074522643aSPetri Gynther return 0; 5084522643aSPetri Gynther 5094522643aSPetri Gynther err_hid: 5104522643aSPetri Gynther hid_destroy_device(hid); 5114522643aSPetri Gynther uhid->hid = NULL; 5124522643aSPetri Gynther uhid->running = false; 5134522643aSPetri Gynther err_free: 5144522643aSPetri Gynther kfree(uhid->rd_data); 51541c4a464SDavid Herrmann uhid->rd_data = NULL; 51641c4a464SDavid Herrmann uhid->rd_size = 0; 5174522643aSPetri Gynther return ret; 5184522643aSPetri Gynther } 5194522643aSPetri Gynther 52056c47754SDavid Herrmann static int uhid_dev_create(struct uhid_device *uhid, 52156c47754SDavid Herrmann struct uhid_event *ev) 52256c47754SDavid Herrmann { 52356c47754SDavid Herrmann struct uhid_create_req orig; 52456c47754SDavid Herrmann 52556c47754SDavid Herrmann orig = ev->u.create; 52656c47754SDavid Herrmann 52756c47754SDavid Herrmann if (orig.rd_size <= 0 || orig.rd_size > HID_MAX_DESCRIPTOR_SIZE) 52856c47754SDavid Herrmann return -EINVAL; 52956c47754SDavid Herrmann if (copy_from_user(&ev->u.create2.rd_data, orig.rd_data, orig.rd_size)) 53056c47754SDavid Herrmann return -EFAULT; 53156c47754SDavid Herrmann 53256c47754SDavid Herrmann memcpy(ev->u.create2.name, orig.name, sizeof(orig.name)); 53356c47754SDavid Herrmann memcpy(ev->u.create2.phys, orig.phys, sizeof(orig.phys)); 53456c47754SDavid Herrmann memcpy(ev->u.create2.uniq, orig.uniq, sizeof(orig.uniq)); 53556c47754SDavid Herrmann ev->u.create2.rd_size = orig.rd_size; 53656c47754SDavid Herrmann ev->u.create2.bus = orig.bus; 53756c47754SDavid Herrmann ev->u.create2.vendor = orig.vendor; 53856c47754SDavid Herrmann ev->u.create2.product = orig.product; 53956c47754SDavid Herrmann ev->u.create2.version = orig.version; 54056c47754SDavid Herrmann ev->u.create2.country = orig.country; 54156c47754SDavid Herrmann 54256c47754SDavid Herrmann return uhid_dev_create2(uhid, ev); 54356c47754SDavid Herrmann } 54456c47754SDavid Herrmann 545d365c6cfSDavid Herrmann static int uhid_dev_destroy(struct uhid_device *uhid) 546d365c6cfSDavid Herrmann { 547d365c6cfSDavid Herrmann if (!uhid->running) 548d365c6cfSDavid Herrmann return -EINVAL; 549d365c6cfSDavid Herrmann 550d365c6cfSDavid Herrmann uhid->running = false; 551fcfcf0deSDavid Herrmann wake_up_interruptible(&uhid->report_wait); 552d365c6cfSDavid Herrmann 553d365c6cfSDavid Herrmann hid_destroy_device(uhid->hid); 554d365c6cfSDavid Herrmann kfree(uhid->rd_data); 555d365c6cfSDavid Herrmann 556d365c6cfSDavid Herrmann return 0; 557d365c6cfSDavid Herrmann } 558d365c6cfSDavid Herrmann 5595e87a36aSDavid Herrmann static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) 5605e87a36aSDavid Herrmann { 5615e87a36aSDavid Herrmann if (!uhid->running) 5625e87a36aSDavid Herrmann return -EINVAL; 5635e87a36aSDavid Herrmann 5645e87a36aSDavid Herrmann hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, 5655e87a36aSDavid Herrmann min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); 5665e87a36aSDavid Herrmann 5675e87a36aSDavid Herrmann return 0; 5685e87a36aSDavid Herrmann } 5695e87a36aSDavid Herrmann 5704522643aSPetri Gynther static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev) 5714522643aSPetri Gynther { 5724522643aSPetri Gynther if (!uhid->running) 5734522643aSPetri Gynther return -EINVAL; 5744522643aSPetri Gynther 5754522643aSPetri Gynther hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, 5764522643aSPetri Gynther min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); 5774522643aSPetri Gynther 5784522643aSPetri Gynther return 0; 5794522643aSPetri Gynther } 5804522643aSPetri Gynther 581fa71f32bSDavid Herrmann static int uhid_dev_get_report_reply(struct uhid_device *uhid, 582fcfcf0deSDavid Herrmann struct uhid_event *ev) 583fcfcf0deSDavid Herrmann { 584fcfcf0deSDavid Herrmann if (!uhid->running) 585fcfcf0deSDavid Herrmann return -EINVAL; 586fcfcf0deSDavid Herrmann 58711c22155SDavid Herrmann uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev); 58811c22155SDavid Herrmann return 0; 58911c22155SDavid Herrmann } 590fcfcf0deSDavid Herrmann 59111c22155SDavid Herrmann static int uhid_dev_set_report_reply(struct uhid_device *uhid, 59211c22155SDavid Herrmann struct uhid_event *ev) 59311c22155SDavid Herrmann { 59411c22155SDavid Herrmann if (!uhid->running) 59511c22155SDavid Herrmann return -EINVAL; 596fcfcf0deSDavid Herrmann 59711c22155SDavid Herrmann uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev); 598fcfcf0deSDavid Herrmann return 0; 599fcfcf0deSDavid Herrmann } 600fcfcf0deSDavid Herrmann 6011ccd7a2aSDavid Herrmann static int uhid_char_open(struct inode *inode, struct file *file) 6021ccd7a2aSDavid Herrmann { 603ace3d861SDavid Herrmann struct uhid_device *uhid; 604ace3d861SDavid Herrmann 605ace3d861SDavid Herrmann uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); 606ace3d861SDavid Herrmann if (!uhid) 607ace3d861SDavid Herrmann return -ENOMEM; 608ace3d861SDavid Herrmann 609d937ae5fSDavid Herrmann mutex_init(&uhid->devlock); 610fcfcf0deSDavid Herrmann mutex_init(&uhid->report_lock); 611ace3d861SDavid Herrmann spin_lock_init(&uhid->qlock); 612ace3d861SDavid Herrmann init_waitqueue_head(&uhid->waitq); 613fcfcf0deSDavid Herrmann init_waitqueue_head(&uhid->report_wait); 614d365c6cfSDavid Herrmann uhid->running = false; 615ace3d861SDavid Herrmann 616ace3d861SDavid Herrmann file->private_data = uhid; 617ace3d861SDavid Herrmann nonseekable_open(inode, file); 618ace3d861SDavid Herrmann 6191ccd7a2aSDavid Herrmann return 0; 6201ccd7a2aSDavid Herrmann } 6211ccd7a2aSDavid Herrmann 6221ccd7a2aSDavid Herrmann static int uhid_char_release(struct inode *inode, struct file *file) 6231ccd7a2aSDavid Herrmann { 624ace3d861SDavid Herrmann struct uhid_device *uhid = file->private_data; 625ace3d861SDavid Herrmann unsigned int i; 626ace3d861SDavid Herrmann 627d365c6cfSDavid Herrmann uhid_dev_destroy(uhid); 628d365c6cfSDavid Herrmann 629ace3d861SDavid Herrmann for (i = 0; i < UHID_BUFSIZE; ++i) 630ace3d861SDavid Herrmann kfree(uhid->outq[i]); 631ace3d861SDavid Herrmann 632ace3d861SDavid Herrmann kfree(uhid); 633ace3d861SDavid Herrmann 6341ccd7a2aSDavid Herrmann return 0; 6351ccd7a2aSDavid Herrmann } 6361ccd7a2aSDavid Herrmann 6371ccd7a2aSDavid Herrmann static ssize_t uhid_char_read(struct file *file, char __user *buffer, 6381ccd7a2aSDavid Herrmann size_t count, loff_t *ppos) 6391ccd7a2aSDavid Herrmann { 640d937ae5fSDavid Herrmann struct uhid_device *uhid = file->private_data; 641d937ae5fSDavid Herrmann int ret; 642d937ae5fSDavid Herrmann unsigned long flags; 643d937ae5fSDavid Herrmann size_t len; 644d937ae5fSDavid Herrmann 645d937ae5fSDavid Herrmann /* they need at least the "type" member of uhid_event */ 646d937ae5fSDavid Herrmann if (count < sizeof(__u32)) 647d937ae5fSDavid Herrmann return -EINVAL; 648d937ae5fSDavid Herrmann 649d937ae5fSDavid Herrmann try_again: 650d937ae5fSDavid Herrmann if (file->f_flags & O_NONBLOCK) { 651d937ae5fSDavid Herrmann if (uhid->head == uhid->tail) 652d937ae5fSDavid Herrmann return -EAGAIN; 653d937ae5fSDavid Herrmann } else { 654d937ae5fSDavid Herrmann ret = wait_event_interruptible(uhid->waitq, 655d937ae5fSDavid Herrmann uhid->head != uhid->tail); 656d937ae5fSDavid Herrmann if (ret) 657d937ae5fSDavid Herrmann return ret; 658d937ae5fSDavid Herrmann } 659d937ae5fSDavid Herrmann 660d937ae5fSDavid Herrmann ret = mutex_lock_interruptible(&uhid->devlock); 661d937ae5fSDavid Herrmann if (ret) 662d937ae5fSDavid Herrmann return ret; 663d937ae5fSDavid Herrmann 664d937ae5fSDavid Herrmann if (uhid->head == uhid->tail) { 665d937ae5fSDavid Herrmann mutex_unlock(&uhid->devlock); 666d937ae5fSDavid Herrmann goto try_again; 667d937ae5fSDavid Herrmann } else { 668d937ae5fSDavid Herrmann len = min(count, sizeof(**uhid->outq)); 669adefb69bSVinicius Costa Gomes if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { 670d937ae5fSDavid Herrmann ret = -EFAULT; 671d937ae5fSDavid Herrmann } else { 672d937ae5fSDavid Herrmann kfree(uhid->outq[uhid->tail]); 673d937ae5fSDavid Herrmann uhid->outq[uhid->tail] = NULL; 674d937ae5fSDavid Herrmann 675d937ae5fSDavid Herrmann spin_lock_irqsave(&uhid->qlock, flags); 676d937ae5fSDavid Herrmann uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; 677d937ae5fSDavid Herrmann spin_unlock_irqrestore(&uhid->qlock, flags); 678d937ae5fSDavid Herrmann } 679d937ae5fSDavid Herrmann } 680d937ae5fSDavid Herrmann 681d937ae5fSDavid Herrmann mutex_unlock(&uhid->devlock); 682d937ae5fSDavid Herrmann return ret ? ret : len; 6831ccd7a2aSDavid Herrmann } 6841ccd7a2aSDavid Herrmann 6851ccd7a2aSDavid Herrmann static ssize_t uhid_char_write(struct file *file, const char __user *buffer, 6861ccd7a2aSDavid Herrmann size_t count, loff_t *ppos) 6871ccd7a2aSDavid Herrmann { 6886664ef72SDavid Herrmann struct uhid_device *uhid = file->private_data; 6896664ef72SDavid Herrmann int ret; 6906664ef72SDavid Herrmann size_t len; 6916664ef72SDavid Herrmann 6926664ef72SDavid Herrmann /* we need at least the "type" member of uhid_event */ 6936664ef72SDavid Herrmann if (count < sizeof(__u32)) 6946664ef72SDavid Herrmann return -EINVAL; 6956664ef72SDavid Herrmann 6966664ef72SDavid Herrmann ret = mutex_lock_interruptible(&uhid->devlock); 6976664ef72SDavid Herrmann if (ret) 6986664ef72SDavid Herrmann return ret; 6996664ef72SDavid Herrmann 7006664ef72SDavid Herrmann memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); 7016664ef72SDavid Herrmann len = min(count, sizeof(uhid->input_buf)); 702befde022SDmitry Torokhov 703befde022SDmitry Torokhov ret = uhid_event_from_user(buffer, len, &uhid->input_buf); 704befde022SDmitry Torokhov if (ret) 7056664ef72SDavid Herrmann goto unlock; 7066664ef72SDavid Herrmann 7076664ef72SDavid Herrmann switch (uhid->input_buf.type) { 708d365c6cfSDavid Herrmann case UHID_CREATE: 709d365c6cfSDavid Herrmann ret = uhid_dev_create(uhid, &uhid->input_buf); 710d365c6cfSDavid Herrmann break; 7114522643aSPetri Gynther case UHID_CREATE2: 7124522643aSPetri Gynther ret = uhid_dev_create2(uhid, &uhid->input_buf); 7134522643aSPetri Gynther break; 714d365c6cfSDavid Herrmann case UHID_DESTROY: 715d365c6cfSDavid Herrmann ret = uhid_dev_destroy(uhid); 716d365c6cfSDavid Herrmann break; 7175e87a36aSDavid Herrmann case UHID_INPUT: 7185e87a36aSDavid Herrmann ret = uhid_dev_input(uhid, &uhid->input_buf); 7195e87a36aSDavid Herrmann break; 7204522643aSPetri Gynther case UHID_INPUT2: 7214522643aSPetri Gynther ret = uhid_dev_input2(uhid, &uhid->input_buf); 7224522643aSPetri Gynther break; 723fa71f32bSDavid Herrmann case UHID_GET_REPORT_REPLY: 724fa71f32bSDavid Herrmann ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf); 725fcfcf0deSDavid Herrmann break; 72611c22155SDavid Herrmann case UHID_SET_REPORT_REPLY: 72711c22155SDavid Herrmann ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf); 72811c22155SDavid Herrmann break; 7296664ef72SDavid Herrmann default: 7306664ef72SDavid Herrmann ret = -EOPNOTSUPP; 7316664ef72SDavid Herrmann } 7326664ef72SDavid Herrmann 7336664ef72SDavid Herrmann unlock: 7346664ef72SDavid Herrmann mutex_unlock(&uhid->devlock); 7356664ef72SDavid Herrmann 7366664ef72SDavid Herrmann /* return "count" not "len" to not confuse the caller */ 7376664ef72SDavid Herrmann return ret ? ret : count; 7381ccd7a2aSDavid Herrmann } 7391ccd7a2aSDavid Herrmann 7401ccd7a2aSDavid Herrmann static unsigned int uhid_char_poll(struct file *file, poll_table *wait) 7411ccd7a2aSDavid Herrmann { 7421f9dec1eSDavid Herrmann struct uhid_device *uhid = file->private_data; 7431f9dec1eSDavid Herrmann 7441f9dec1eSDavid Herrmann poll_wait(file, &uhid->waitq, wait); 7451f9dec1eSDavid Herrmann 7461f9dec1eSDavid Herrmann if (uhid->head != uhid->tail) 7471f9dec1eSDavid Herrmann return POLLIN | POLLRDNORM; 7481f9dec1eSDavid Herrmann 7491ccd7a2aSDavid Herrmann return 0; 7501ccd7a2aSDavid Herrmann } 7511ccd7a2aSDavid Herrmann 7521ccd7a2aSDavid Herrmann static const struct file_operations uhid_fops = { 7531ccd7a2aSDavid Herrmann .owner = THIS_MODULE, 7541ccd7a2aSDavid Herrmann .open = uhid_char_open, 7551ccd7a2aSDavid Herrmann .release = uhid_char_release, 7561ccd7a2aSDavid Herrmann .read = uhid_char_read, 7571ccd7a2aSDavid Herrmann .write = uhid_char_write, 7581ccd7a2aSDavid Herrmann .poll = uhid_char_poll, 7591ccd7a2aSDavid Herrmann .llseek = no_llseek, 7601ccd7a2aSDavid Herrmann }; 7611ccd7a2aSDavid Herrmann 7621ccd7a2aSDavid Herrmann static struct miscdevice uhid_misc = { 7631ccd7a2aSDavid Herrmann .fops = &uhid_fops, 76419872d20SDavid Herrmann .minor = UHID_MINOR, 7651ccd7a2aSDavid Herrmann .name = UHID_NAME, 7661ccd7a2aSDavid Herrmann }; 7671ccd7a2aSDavid Herrmann 7681ccd7a2aSDavid Herrmann static int __init uhid_init(void) 7691ccd7a2aSDavid Herrmann { 7701ccd7a2aSDavid Herrmann return misc_register(&uhid_misc); 7711ccd7a2aSDavid Herrmann } 7721ccd7a2aSDavid Herrmann 7731ccd7a2aSDavid Herrmann static void __exit uhid_exit(void) 7741ccd7a2aSDavid Herrmann { 7751ccd7a2aSDavid Herrmann misc_deregister(&uhid_misc); 7761ccd7a2aSDavid Herrmann } 7771ccd7a2aSDavid Herrmann 7781ccd7a2aSDavid Herrmann module_init(uhid_init); 7791ccd7a2aSDavid Herrmann module_exit(uhid_exit); 7801ccd7a2aSDavid Herrmann MODULE_LICENSE("GPL"); 7811ccd7a2aSDavid Herrmann MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); 7821ccd7a2aSDavid Herrmann MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); 78319872d20SDavid Herrmann MODULE_ALIAS_MISCDEV(UHID_MINOR); 78460cbd53eSMarcel Holtmann MODULE_ALIAS("devname:" UHID_NAME); 785