linux/drivers/hid/hidraw.c
<<
>>
Prefs
   1/*
   2 * HID raw devices, giving access to raw HID events.
   3 *
   4 * In comparison to hiddev, this device does not process the
   5 * hid events at all (no parsing, no lookups). This lets applications
   6 * to work on raw hid events as they want to, and avoids a need to
   7 * use a transport-specific userspace libhid/libusb libraries.
   8 *
   9 *  Copyright (c) 2007 Jiri Kosina
  10 */
  11
  12/*
  13 * This program is free software; you can redistribute it and/or modify it
  14 * under the terms and conditions of the GNU General Public License,
  15 * version 2, as published by the Free Software Foundation.
  16 *
  17 * You should have received a copy of the GNU General Public License along with
  18 * this program; if not, write to the Free Software Foundation, Inc.,
  19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  20 */
  21
  22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  23
  24#include <linux/fs.h>
  25#include <linux/module.h>
  26#include <linux/errno.h>
  27#include <linux/kernel.h>
  28#include <linux/init.h>
  29#include <linux/cdev.h>
  30#include <linux/poll.h>
  31#include <linux/device.h>
  32#include <linux/major.h>
  33#include <linux/slab.h>
  34#include <linux/hid.h>
  35#include <linux/mutex.h>
  36#include <linux/sched.h>
  37
  38#include <linux/hidraw.h>
  39
  40static int hidraw_major;
  41static struct cdev hidraw_cdev;
  42static struct class *hidraw_class;
  43static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
  44static DEFINE_MUTEX(minors_lock);
  45static void drop_ref(struct hidraw *hid, int exists_bit);
  46
  47static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
  48{
  49        struct hidraw_list *list = file->private_data;
  50        int ret = 0, len;
  51        DECLARE_WAITQUEUE(wait, current);
  52
  53        mutex_lock(&list->read_mutex);
  54
  55        while (ret == 0) {
  56                if (list->head == list->tail) {
  57                        add_wait_queue(&list->hidraw->wait, &wait);
  58                        set_current_state(TASK_INTERRUPTIBLE);
  59
  60                        while (list->head == list->tail) {
  61                                if (file->f_flags & O_NONBLOCK) {
  62                                        ret = -EAGAIN;
  63                                        break;
  64                                }
  65                                if (signal_pending(current)) {
  66                                        ret = -ERESTARTSYS;
  67                                        break;
  68                                }
  69                                if (!list->hidraw->exist) {
  70                                        ret = -EIO;
  71                                        break;
  72                                }
  73
  74                                /* allow O_NONBLOCK to work well from other threads */
  75                                mutex_unlock(&list->read_mutex);
  76                                schedule();
  77                                mutex_lock(&list->read_mutex);
  78                                set_current_state(TASK_INTERRUPTIBLE);
  79                        }
  80
  81                        set_current_state(TASK_RUNNING);
  82                        remove_wait_queue(&list->hidraw->wait, &wait);
  83                }
  84
  85                if (ret)
  86                        goto out;
  87
  88                len = list->buffer[list->tail].len > count ?
  89                        count : list->buffer[list->tail].len;
  90
  91                if (list->buffer[list->tail].value) {
  92                        if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
  93                                ret = -EFAULT;
  94                                goto out;
  95                        }
  96                        ret = len;
  97                }
  98
  99                kfree(list->buffer[list->tail].value);
 100                list->buffer[list->tail].value = NULL;
 101                list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
 102        }
 103out:
 104        mutex_unlock(&list->read_mutex);
 105        return ret;
 106}
 107
 108/* The first byte is expected to be a report number.
 109 * This function is to be called with the minors_lock mutex held */
 110static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
 111{
 112        unsigned int minor = iminor(file->f_path.dentry->d_inode);
 113        struct hid_device *dev;
 114        __u8 *buf;
 115        int ret = 0;
 116
 117        if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
 118                ret = -ENODEV;
 119                goto out;
 120        }
 121
 122        dev = hidraw_table[minor]->hid;
 123
 124        if (!dev->hid_output_raw_report) {
 125                ret = -ENODEV;
 126                goto out;
 127        }
 128
 129        if (count > HID_MAX_BUFFER_SIZE) {
 130                hid_warn(dev, "pid %d passed too large report\n",
 131                         task_pid_nr(current));
 132                ret = -EINVAL;
 133                goto out;
 134        }
 135
 136        if (count < 2) {
 137                hid_warn(dev, "pid %d passed too short report\n",
 138                         task_pid_nr(current));
 139                ret = -EINVAL;
 140                goto out;
 141        }
 142
 143        buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
 144        if (!buf) {
 145                ret = -ENOMEM;
 146                goto out;
 147        }
 148
 149        if (copy_from_user(buf, buffer, count)) {
 150                ret = -EFAULT;
 151                goto out_free;
 152        }
 153
 154        ret = dev->hid_output_raw_report(dev, buf, count, report_type);
 155out_free:
 156        kfree(buf);
 157out:
 158        return ret;
 159}
 160
 161/* the first byte is expected to be a report number */
 162static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
 163{
 164        ssize_t ret;
 165        mutex_lock(&minors_lock);
 166        ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
 167        mutex_unlock(&minors_lock);
 168        return ret;
 169}
 170
 171
 172/* This function performs a Get_Report transfer over the control endpoint
 173 * per section 7.2.1 of the HID specification, version 1.1.  The first byte
 174 * of buffer is the report number to request, or 0x0 if the defice does not
 175 * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
 176 * or HID_INPUT_REPORT.  This function is to be called with the minors_lock
 177 *  mutex held. */
 178static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
 179{
 180        unsigned int minor = iminor(file->f_path.dentry->d_inode);
 181        struct hid_device *dev;
 182        __u8 *buf;
 183        int ret = 0, len;
 184        unsigned char report_number;
 185
 186        dev = hidraw_table[minor]->hid;
 187
 188        if (!dev->hid_get_raw_report) {
 189                ret = -ENODEV;
 190                goto out;
 191        }
 192
 193        if (count > HID_MAX_BUFFER_SIZE) {
 194                printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
 195                                task_pid_nr(current));
 196                ret = -EINVAL;
 197                goto out;
 198        }
 199
 200        if (count < 2) {
 201                printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
 202                                task_pid_nr(current));
 203                ret = -EINVAL;
 204                goto out;
 205        }
 206
 207        buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
 208        if (!buf) {
 209                ret = -ENOMEM;
 210                goto out;
 211        }
 212
 213        /* Read the first byte from the user. This is the report number,
 214         * which is passed to dev->hid_get_raw_report(). */
 215        if (copy_from_user(&report_number, buffer, 1)) {
 216                ret = -EFAULT;
 217                goto out_free;
 218        }
 219
 220        ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
 221
 222        if (ret < 0)
 223                goto out_free;
 224
 225        len = (ret < count) ? ret : count;
 226
 227        if (copy_to_user(buffer, buf, len)) {
 228                ret = -EFAULT;
 229                goto out_free;
 230        }
 231
 232        ret = len;
 233
 234out_free:
 235        kfree(buf);
 236out:
 237        return ret;
 238}
 239
 240static unsigned int hidraw_poll(struct file *file, poll_table *wait)
 241{
 242        struct hidraw_list *list = file->private_data;
 243
 244        poll_wait(file, &list->hidraw->wait, wait);
 245        if (list->head != list->tail)
 246                return POLLIN | POLLRDNORM;
 247        if (!list->hidraw->exist)
 248                return POLLERR | POLLHUP;
 249        return 0;
 250}
 251
 252static int hidraw_open(struct inode *inode, struct file *file)
 253{
 254        unsigned int minor = iminor(inode);
 255        struct hidraw *dev;
 256        struct hidraw_list *list;
 257        int err = 0;
 258
 259        if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
 260                err = -ENOMEM;
 261                goto out;
 262        }
 263
 264        mutex_lock(&minors_lock);
 265        if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
 266                err = -ENODEV;
 267                goto out_unlock;
 268        }
 269
 270        list->hidraw = hidraw_table[minor];
 271        mutex_init(&list->read_mutex);
 272        list_add_tail(&list->node, &hidraw_table[minor]->list);
 273        file->private_data = list;
 274
 275        dev = hidraw_table[minor];
 276        if (!dev->open++) {
 277                err = hid_hw_power(dev->hid, PM_HINT_FULLON);
 278                if (err < 0) {
 279                        dev->open--;
 280                        goto out_unlock;
 281                }
 282
 283                err = hid_hw_open(dev->hid);
 284                if (err < 0) {
 285                        hid_hw_power(dev->hid, PM_HINT_NORMAL);
 286                        dev->open--;
 287                }
 288        }
 289
 290out_unlock:
 291        mutex_unlock(&minors_lock);
 292out:
 293        if (err < 0)
 294                kfree(list);
 295        return err;
 296
 297}
 298
 299static int hidraw_release(struct inode * inode, struct file * file)
 300{
 301        unsigned int minor = iminor(inode);
 302        struct hidraw_list *list = file->private_data;
 303
 304        drop_ref(hidraw_table[minor], 0);
 305        list_del(&list->node);
 306        kfree(list);
 307        return 0;
 308}
 309
 310static long hidraw_ioctl(struct file *file, unsigned int cmd,
 311                                                        unsigned long arg)
 312{
 313        struct inode *inode = file->f_path.dentry->d_inode;
 314        unsigned int minor = iminor(inode);
 315        long ret = 0;
 316        struct hidraw *dev;
 317        void __user *user_arg = (void __user*) arg;
 318
 319        mutex_lock(&minors_lock);
 320        dev = hidraw_table[minor];
 321        if (!dev) {
 322                ret = -ENODEV;
 323                goto out;
 324        }
 325
 326        switch (cmd) {
 327                case HIDIOCGRDESCSIZE:
 328                        if (put_user(dev->hid->rsize, (int __user *)arg))
 329                                ret = -EFAULT;
 330                        break;
 331
 332                case HIDIOCGRDESC:
 333                        {
 334                                __u32 len;
 335
 336                                if (get_user(len, (int __user *)arg))
 337                                        ret = -EFAULT;
 338                                else if (len > HID_MAX_DESCRIPTOR_SIZE - 1)
 339                                        ret = -EINVAL;
 340                                else if (copy_to_user(user_arg + offsetof(
 341                                        struct hidraw_report_descriptor,
 342                                        value[0]),
 343                                        dev->hid->rdesc,
 344                                        min(dev->hid->rsize, len)))
 345                                        ret = -EFAULT;
 346                                break;
 347                        }
 348                case HIDIOCGRAWINFO:
 349                        {
 350                                struct hidraw_devinfo dinfo;
 351
 352                                dinfo.bustype = dev->hid->bus;
 353                                dinfo.vendor = dev->hid->vendor;
 354                                dinfo.product = dev->hid->product;
 355                                if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
 356                                        ret = -EFAULT;
 357                                break;
 358                        }
 359                default:
 360                        {
 361                                struct hid_device *hid = dev->hid;
 362                                if (_IOC_TYPE(cmd) != 'H') {
 363                                        ret = -EINVAL;
 364                                        break;
 365                                }
 366
 367                                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {
 368                                        int len = _IOC_SIZE(cmd);
 369                                        ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
 370                                        break;
 371                                }
 372                                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {
 373                                        int len = _IOC_SIZE(cmd);
 374                                        ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
 375                                        break;
 376                                }
 377
 378                                /* Begin Read-only ioctls. */
 379                                if (_IOC_DIR(cmd) != _IOC_READ) {
 380                                        ret = -EINVAL;
 381                                        break;
 382                                }
 383
 384                                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {
 385                                        int len = strlen(hid->name) + 1;
 386                                        if (len > _IOC_SIZE(cmd))
 387                                                len = _IOC_SIZE(cmd);
 388                                        ret = copy_to_user(user_arg, hid->name, len) ?
 389                                                -EFAULT : len;
 390                                        break;
 391                                }
 392
 393                                if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {
 394                                        int len = strlen(hid->phys) + 1;
 395                                        if (len > _IOC_SIZE(cmd))
 396                                                len = _IOC_SIZE(cmd);
 397                                        ret = copy_to_user(user_arg, hid->phys, len) ?
 398                                                -EFAULT : len;
 399                                        break;
 400                                }
 401                        }
 402
 403                ret = -ENOTTY;
 404        }
 405out:
 406        mutex_unlock(&minors_lock);
 407        return ret;
 408}
 409
 410static const struct file_operations hidraw_ops = {
 411        .owner =        THIS_MODULE,
 412        .read =         hidraw_read,
 413        .write =        hidraw_write,
 414        .poll =         hidraw_poll,
 415        .open =         hidraw_open,
 416        .release =      hidraw_release,
 417        .unlocked_ioctl = hidraw_ioctl,
 418#ifdef CONFIG_COMPAT
 419        .compat_ioctl   = hidraw_ioctl,
 420#endif
 421        .llseek =       noop_llseek,
 422};
 423
 424int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
 425{
 426        struct hidraw *dev = hid->hidraw;
 427        struct hidraw_list *list;
 428        int ret = 0;
 429
 430        list_for_each_entry(list, &dev->list, node) {
 431                int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
 432
 433                if (new_head == list->tail)
 434                        continue;
 435
 436                if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {
 437                        ret = -ENOMEM;
 438                        break;
 439                }
 440                list->buffer[list->head].len = len;
 441                list->head = new_head;
 442                kill_fasync(&list->fasync, SIGIO, POLL_IN);
 443        }
 444
 445        wake_up_interruptible(&dev->wait);
 446        return ret;
 447}
 448EXPORT_SYMBOL_GPL(hidraw_report_event);
 449
 450int hidraw_connect(struct hid_device *hid)
 451{
 452        int minor, result;
 453        struct hidraw *dev;
 454
 455        /* we accept any HID device, no matter the applications */
 456
 457        dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
 458        if (!dev)
 459                return -ENOMEM;
 460
 461        result = -EINVAL;
 462
 463        mutex_lock(&minors_lock);
 464
 465        for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
 466                if (hidraw_table[minor])
 467                        continue;
 468                hidraw_table[minor] = dev;
 469                result = 0;
 470                break;
 471        }
 472
 473        if (result) {
 474                mutex_unlock(&minors_lock);
 475                kfree(dev);
 476                goto out;
 477        }
 478
 479        dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
 480                                 NULL, "%s%d", "hidraw", minor);
 481
 482        if (IS_ERR(dev->dev)) {
 483                hidraw_table[minor] = NULL;
 484                mutex_unlock(&minors_lock);
 485                result = PTR_ERR(dev->dev);
 486                kfree(dev);
 487                goto out;
 488        }
 489
 490        mutex_unlock(&minors_lock);
 491        init_waitqueue_head(&dev->wait);
 492        INIT_LIST_HEAD(&dev->list);
 493
 494        dev->hid = hid;
 495        dev->minor = minor;
 496
 497        dev->exist = 1;
 498        hid->hidraw = dev;
 499
 500out:
 501        return result;
 502
 503}
 504EXPORT_SYMBOL_GPL(hidraw_connect);
 505
 506void hidraw_disconnect(struct hid_device *hid)
 507{
 508        struct hidraw *hidraw = hid->hidraw;
 509        drop_ref(hidraw, 1);
 510}
 511EXPORT_SYMBOL_GPL(hidraw_disconnect);
 512
 513int __init hidraw_init(void)
 514{
 515        int result;
 516        dev_t dev_id;
 517
 518        result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
 519                        HIDRAW_MAX_DEVICES, "hidraw");
 520
 521        hidraw_major = MAJOR(dev_id);
 522
 523        if (result < 0) {
 524                pr_warn("can't get major number\n");
 525                result = 0;
 526                goto out;
 527        }
 528
 529        hidraw_class = class_create(THIS_MODULE, "hidraw");
 530        if (IS_ERR(hidraw_class)) {
 531                result = PTR_ERR(hidraw_class);
 532                unregister_chrdev(hidraw_major, "hidraw");
 533                goto out;
 534        }
 535
 536        cdev_init(&hidraw_cdev, &hidraw_ops);
 537        cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
 538out:
 539        return result;
 540}
 541
 542void hidraw_exit(void)
 543{
 544        dev_t dev_id = MKDEV(hidraw_major, 0);
 545
 546        cdev_del(&hidraw_cdev);
 547        class_destroy(hidraw_class);
 548        unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
 549
 550}
 551
 552static void drop_ref(struct hidraw *hidraw, int exists_bit)
 553{
 554        mutex_lock(&minors_lock);
 555        if (exists_bit) {
 556                hid_hw_close(hidraw->hid);
 557                hidraw->exist = 0;
 558                if (hidraw->open)
 559                        wake_up_interruptible(&hidraw->wait);
 560        } else {
 561                --hidraw->open;
 562        }
 563
 564        if (!hidraw->open && !hidraw->exist) {
 565                device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
 566                hidraw_table[hidraw->minor] = NULL;
 567                kfree(hidraw);
 568        }
 569        mutex_unlock(&minors_lock);
 570}
 571
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.