/*
* Copyright (c) 2001 Paul Stewart
* Copyright (c) 2001 Vojtech Pavlik
*
* HID char devices, giving access to raw HID device events.
*
*/
/*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
*/
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include <linux/hiddev.h>
#include <linux/compat.h>
#include "usbhid.h"
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define HIDDEV_MINOR_BASE 0
#define HIDDEV_MINORS 256
#else
#define HIDDEV_MINOR_BASE 96
#define HIDDEV_MINORS 16
#endif
#define HIDDEV_BUFFER_SIZE 2048
struct hiddev {
int exist;
int open;
struct mutex existancelock;
wait_queue_head_t wait;
struct hid_device *hid;
struct list_head list;
spinlock_t list_lock;
};
struct hiddev_list {
struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
int head;
int tail;
unsigned flags;
struct fasync_struct *fasync;
struct hiddev *hiddev;
struct list_head node;
struct mutex thread_lock;
};
static struct usb_driver hiddev_driver;
/*
* Find a report, given the report's type and ID. The ID can be specified
* indirectly by REPORT_ID_FIRST (which returns the first report of the given
* type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
* given type which follows old_id.
*/
static struct hid_report *
hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
{
unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
struct hid_report_enum *report_enum;
struct hid_report *report;
struct list_head *list;
if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
rinfo->report_type > HID_REPORT_TYPE_MAX)
return NULL;
report_enum = hid->report_enum +
(rinfo->report_type - HID_REPORT_TYPE_MIN);
switch (flags) {
case 0: /* Nothing to do -- report_id is already set correctly */
break;
case HID_REPORT_ID_FIRST:
if (list_empty(&report_enum->report_list))
return NULL;
list = report_enum->report_list.next;
report = list_entry(list, struct hid_report, list);
rinfo->report_id = report->id;
break;
case HID_REPORT_ID_NEXT:
report = report_enum->report_id_hash[rid];
if (!report)
return NULL;
list = report->list.next;
if (list == &report_enum->report_list)
return NULL;
report = list_entry(list, struct hid_report, list);
rinfo->report_id = report->id;
break;
default:
return NULL;
}
return report_enum->report_id_hash[rinfo->report_id];
}
/*
* Perform an exhaustive search of the report table for a usage, given its
* type and usage id.
*/
static struct hid_field *
hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
{
int i, j;
struct hid_report *report;
struct hid_report_enum *report_enum;
struct hid_field *field;
if (uref->report_type < HID_REPORT_TYPE_MIN ||
uref->report_type > HID_REPORT_TYPE_MAX)
return NULL;
report_enum = hid->report_enum +
(uref->report_type - HID_REPORT_TYPE_MIN);
list_for_each_entry(report, &report_enum->report_list, list) {
for (i = 0; i < report->maxfield; i++) {
field = report->field[i];
for (j = 0; j < field->maxusage; j++) {
if (field->usage[j].hid == uref->usage_code) {
uref->report_id = report->id;
uref->field_index = i;
uref->usage_index = j;
return field;
}
}
}
}
return NULL;
}
static void hiddev_send_event(struct hid_device *hid,
struct hiddev_usage_ref *uref)
{
struct hiddev *hiddev = hid->hiddev;
struct hiddev_list *list;
unsigned long flags;
spin_lock_irqsave(&hiddev->list_lock, flags);
list_for_each_entry(list, &hiddev->list, node) {