/*
- * $Id: hid-core.c,v 1.8 2001/05/23 12:02:18 vojtech Exp $
+ * $Id: hid-core.c,v 1.42 2002/01/27 00:22:46 vojtech Exp $
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* USB HID support for Linux
- *
- * Sponsored by SuSE
*/
/*
* 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
* Version Information
*/
-#define DRIVER_VERSION "v1.8"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik <vojtech@suse.cz>"
-#define DRIVER_DESC "USB HID support drivers"
+#define DRIVER_VERSION "v1.31"
+#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB HID core driver"
+#define DRIVER_LICENSE "GPL"
static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
return -1;
}
- if (HID_MAIN_ITEM_VARIABLE & ~flags) { /* ARRAY */
- if (parser->global.logical_maximum <= parser->global.logical_minimum) {
- dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
- return -1;
- }
- usages = parser->local.usage_index;
- /* Hint: we can assume usages < MAX_USAGE here */
- } else { /* VARIABLE */
- usages = parser->global.report_count;
+ if (parser->global.logical_maximum <= parser->global.logical_minimum) {
+ dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
+ return -1;
}
+ usages = parser->local.usage_index;
offset = report->size;
report->size += parser->global.report_size * parser->global.report_count;
return 0;
case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
- parser->global.logical_maximum = item_sdata(item);
+ if (parser->global.logical_minimum < 0)
+ parser->global.logical_maximum = item_sdata(item);
+ else
+ parser->global.logical_maximum = item_udata(item);
return 0;
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
return 0;
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
- parser->global.physical_maximum = item_sdata(item);
+ if (parser->global.physical_minimum < 0)
+ parser->global.physical_maximum = item_sdata(item);
+ else
+ parser->global.physical_maximum = item_udata(item);
return 0;
case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
- parser->global.unit_exponent = item_udata(item);
+ parser->global.unit_exponent = item_sdata(item);
return 0;
case HID_GLOBAL_ITEM_TAG_UNIT:
for (n = 0; n < report->maxfield; n++)
kfree(report->field[n]);
- if (report->data)
- kfree(report->data);
kfree(report);
}
* items, though they are not used yet.
*/
-static __u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
+static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
{
- if ((end - start) > 0) {
+ u8 b;
- __u8 b = *start++;
- item->type = (b >> 2) & 3;
- item->tag = (b >> 4) & 15;
+ if ((end - start) <= 0)
+ return NULL;
- if (item->tag == HID_ITEM_TAG_LONG) {
+ b = *start++;
- item->format = HID_ITEM_FORMAT_LONG;
+ item->type = (b >> 2) & 3;
+ item->tag = (b >> 4) & 15;
- if ((end - start) >= 2) {
+ if (item->tag == HID_ITEM_TAG_LONG) {
- item->size = *start++;
- item->tag = *start++;
+ item->format = HID_ITEM_FORMAT_LONG;
- if ((end - start) >= item->size) {
- item->data.longdata = start;
- start += item->size;
- return start;
- }
- }
- } else {
+ if ((end - start) < 2)
+ return NULL;
- item->format = HID_ITEM_FORMAT_SHORT;
- item->size = b & 3;
- switch (item->size) {
-
- case 0:
- return start;
-
- case 1:
- if ((end - start) >= 1) {
- item->data.u8 = *start++;
- return start;
- }
- break;
-
- case 2:
- if ((end - start) >= 2) {
- item->data.u16 = le16_to_cpu( get_unaligned(((__u16*)start)++));
- return start;
- }
-
- case 3:
- item->size++;
- if ((end - start) >= 4) {
- item->data.u32 = le32_to_cpu( get_unaligned(((__u32*)start)++));
- return start;
- }
- }
- }
+ item->size = *start++;
+ item->tag = *start++;
+
+ if ((end - start) < item->size)
+ return NULL;
+
+ item->data.longdata = start;
+ start += item->size;
+ return start;
+ }
+
+ item->format = HID_ITEM_FORMAT_SHORT;
+ item->size = b & 3;
+
+ switch (item->size) {
+
+ case 0:
+ return start;
+
+ case 1:
+ if ((end - start) < 1)
+ return NULL;
+ item->data.u8 = *start++;
+ return start;
+
+ case 2:
+ if ((end - start) < 2)
+ return NULL;
+ item->data.u16 = le16_to_cpu(get_unaligned(((__u16*)start)++));
+ return start;
+
+ case 3:
+ item->size++;
+ if ((end - start) < 4)
+ return NULL;
+ item->data.u32 = le32_to_cpu(get_unaligned(((__u32*)start)++));
+ return start;
}
+
return NULL;
}
end = start + size;
while ((start = fetch_item(start, end, &item)) != 0) {
+
if (item.format != HID_ITEM_FORMAT_SHORT) {
dbg("unexpected long global item");
hid_free_device(device);
kfree(parser);
return NULL;
}
+
if (dispatch_type[item.type](parser, &item)) {
dbg("item %u %u %u %u parsing failed\n",
item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
#endif
}
-
/*
* Analyse a received field, and fetch the data from it. The field
* content is stored for next report processing (we do differential
memcpy(field->value, value, count * sizeof(__s32));
}
-static int hid_input_report(int type, u8 *data, int len, struct hid_device *hid)
+static int hid_input_report(int type, struct urb *urb)
{
+ struct hid_device *hid = urb->context;
struct hid_report_enum *report_enum = hid->report_enum + type;
+ u8 *data = urb->transfer_buffer;
+ int len = urb->actual_length;
struct hid_report *report;
int n, size;
len--;
}
- if (!(report = report_enum->report_id_hash[n])) {
- dbg("undefined report_id %d received", n);
-#ifdef DEBUG
- printk(KERN_DEBUG __FILE__ ": report (size %u) = ", len);
- for (n = 0; n < len; n++)
- printk(" %02x", data[n]);
- printk("\n");
+#ifdef DEBUG_DATA
+ {
+ int i;
+ printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, len);
+ for (i = 0; i < n; i++)
+ printk(" %02x", data[i]);
+ printk("\n");
+ }
#endif
+ if (!(report = report_enum->report_id_hash[n])) {
+ dbg("undefined report_id %d received", n);
return -1;
}
size = ((report->size - 1) >> 3) + 1;
if (len < size) {
-
- if (size <= 8) {
- dbg("report %d is too short, (%d < %d)", report->id, len, size);
- return -1;
- }
-
- /*
- * Some low-speed devices have large reports and maxpacketsize 8.
- * We buffer the data in that case and parse it when we got it all.
- * Works only for unnumbered reports. Doesn't make sense for numbered
- * reports anyway - then they don't need to be large.
- */
-
- if (!report->data)
- if (!(report->data = kmalloc(size, GFP_ATOMIC))) {
- dbg("couldn't allocate report buffer");
- return -1;
- }
-
- if (report->idx + len > size) {
- dbg("report data buffer overflow");
- report->idx = 0;
- return -1;
- }
-
- memcpy(report->data + report->idx, data, len);
- report->idx += len;
-
- if (report->idx < size)
- return 0;
-
- data = report->data;
+ dbg("report %d is too short, (%d < %d)", report->id, len, size);
+ return -1;
}
for (n = 0; n < report->maxfield; n++)
hid_input_field(hid, report->field[n], data);
- report->idx = 0;
return 0;
}
/*
- * Interrupt input handler.
+ * Input interrupt completion handler.
*/
-static void hid_irq(struct urb *urb)
+static void hid_irq_in(struct urb *urb)
{
if (urb->status) {
- dbg("nonzero status in irq %d", urb->status);
+ dbg("nonzero status in input irq %d", urb->status);
return;
}
- hid_input_report(HID_INPUT_REPORT, urb->transfer_buffer, urb->actual_length, urb->context);
-}
-
-/*
- * hid_read_report() reads in report values without waiting for an irq urb.
- */
-
-void hid_read_report(struct hid_device *hid, struct hid_report *report)
-{
- int len = ((report->size - 1) >> 3) + 1 + hid->report_enum[report->type].numbered;
- u8 data[len];
- int read;
-
- if ((read = hid_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, data, len)) != len) {
- dbg("reading report type %d id %d failed len %d read %d", report->type + 1, report->id, len, read);
- return;
- }
-
- hid_input_report(report->type, data, len, hid);
+ hid_input_report(HID_INPUT_REPORT, urb);
}
/*
hid_dump_input(field->usage + offset, value);
if (offset >= field->report_count) {
- dbg("offset exceeds report_count");
+ dbg("offset (%d) exceeds report_count (%d)", offset, field->report_count);
+ hid_dump_field(field, 8);
return -1;
}
if (field->logical_minimum < 0) {
return -1;
}
}
- if ( (value > field->logical_maximum)
- || (value < field->logical_minimum)) {
- dbg("value %d is invalid", value);
- return -1;
- }
field->value[offset] = value;
return 0;
}
return -1;
}
+/*
+ * Find a report with a specified HID usage.
+ */
+
+int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct hid_report **report, int type)
+{
+ struct hid_report_enum *report_enum = hid->report_enum + type;
+ struct list_head *list = report_enum->report_list.next;
+ int i, j;
+
+ while (list != &report_enum->report_list) {
+ *report = (struct hid_report *) list;
+ list = list->next;
+ for (i = 0; i < (*report)->maxfield; i++) {
+ struct hid_field *field = (*report)->field[i];
+ for (j = 0; j < field->maxusage; j++)
+ if (field->logical == wanted_usage)
+ return j;
+ }
+ }
+ return -1;
+}
+
+int hid_find_field_in_report(struct hid_report *report, __u32 wanted_usage, struct hid_field **field)
+{
+ int i, j;
+
+ for (i = 0; i < report->maxfield; i++) {
+ *field = report->field[i];
+ for (j = 0; j < (*field)->maxusage; j++)
+ if ((*field)->usage[j].hid == wanted_usage)
+ return j;
+ }
+
+ return -1;
+}
+
static int hid_submit_out(struct hid_device *hid)
{
- hid->urbout.transfer_buffer_length = le16_to_cpup(&hid->out[hid->outtail].dr.wLength);
- hid->urbout.transfer_buffer = hid->out[hid->outtail].buffer;
- hid->urbout.setup_packet = (void *) &(hid->out[hid->outtail].dr);
- hid->urbout.dev = hid->dev;
+ struct hid_report *report;
+
+ report = hid->out[hid->outtail];
+
+ hid_output_report(report, hid->outbuf);
+ hid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1;
+ hid->urbout->dev = hid->dev;
- if (usb_submit_urb(&hid->urbout, GFP_KERNEL)) {
+ dbg("submitting out urb");
+
+ if (usb_submit_urb(hid->urbout, GFP_ATOMIC)) {
err("usb_submit_urb(out) failed");
return -1;
}
return 0;
}
+static int hid_submit_ctrl(struct hid_device *hid)
+{
+ struct hid_report *report;
+ unsigned char dir;
+
+ report = hid->ctrl[hid->ctrltail].report;
+ dir = hid->ctrl[hid->ctrltail].dir;
+
+ if (dir == USB_DIR_OUT)
+ hid_output_report(report, hid->ctrlbuf);
+
+ hid->urbctrl->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + ((report->id > 0) && (dir != USB_DIR_OUT));
+ hid->urbctrl->pipe = (dir == USB_DIR_OUT) ? usb_sndctrlpipe(hid->dev, 0) : usb_rcvctrlpipe(hid->dev, 0);
+ hid->urbctrl->dev = hid->dev;
+
+ hid->cr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
+ hid->cr.bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
+ hid->cr.wValue = ((report->type + 1) << 8) | report->id;
+ hid->cr.wIndex = cpu_to_le16(hid->ifnum);
+ hid->cr.wLength = cpu_to_le16(hid->urbctrl->transfer_buffer_length);
+
+ dbg("submitting ctrl urb");
+
+ if (usb_submit_urb(hid->urbctrl, GFP_ATOMIC)) {
+ err("usb_submit_urb(ctrl) failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Output interrupt completion handler.
+ */
+
+static void hid_irq_out(struct urb *urb)
+{
+ struct hid_device *hid = urb->context;
+ unsigned long flags;
+
+ if (urb->status)
+ warn("output irq status %d received", urb->status);
+
+ spin_lock_irqsave(&hid->outlock, flags);
+
+ hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
+
+ if (hid->outhead != hid->outtail) {
+ hid_submit_out(hid);
+ return;
+ }
+
+ clear_bit(HID_OUT_RUNNING, &hid->iofl);
+
+ spin_unlock_irqrestore(&hid->outlock, flags);
+
+ wake_up(&hid->wait);
+}
+
+/*
+ * Control pipe completion handler.
+ */
+
static void hid_ctrl(struct urb *urb)
{
struct hid_device *hid = urb->context;
+ unsigned long flags;
if (urb->status)
warn("ctrl urb status %d received", urb->status);
- hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+ spin_lock_irqsave(&hid->ctrllock, flags);
- if (hid->outhead != hid->outtail)
- hid_submit_out(hid);
+ if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN)
+ hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb);
+
+ hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+ if (hid->ctrlhead != hid->ctrltail) {
+ hid_submit_ctrl(hid);
+ return;
+ }
+
+ clear_bit(HID_CTRL_RUNNING, &hid->iofl);
+
+ spin_unlock_irqrestore(&hid->ctrllock, flags);
+
+ wake_up(&hid->wait);
}
-void hid_write_report(struct hid_device *hid, struct hid_report *report)
+void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
{
- hid_output_report(report, hid->out[hid->outhead].buffer);
+ int head;
+ unsigned long flags;
- hid->out[hid->outhead].dr.wValue = cpu_to_le16(0x200 | report->id);
- hid->out[hid->outhead].dr.wLength = cpu_to_le16((report->size + 7) >> 3);
+ if (hid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
- hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+ spin_lock_irqsave(&hid->outlock, flags);
- if (hid->outhead == hid->outtail)
- hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+ if ((head = (hid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == hid->outtail) {
+ spin_unlock_irqrestore(&hid->outlock, flags);
+ warn("output queue full");
+ return;
+ }
- if (hid->urbout.status != -EINPROGRESS)
- hid_submit_out(hid);
+ hid->out[hid->outhead] = report;
+ hid->outhead = head;
+
+ if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl))
+ hid_submit_out(hid);
+
+ spin_unlock_irqrestore(&hid->outlock, flags);
+ return;
+ }
+
+ spin_lock_irqsave(&hid->ctrllock, flags);
+
+ if ((head = (hid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == hid->ctrltail) {
+ spin_unlock_irqrestore(&hid->ctrllock, flags);
+ warn("control queue full");
+ return;
+ }
+
+ hid->ctrl[hid->ctrlhead].report = report;
+ hid->ctrl[hid->ctrlhead].dir = dir;
+ hid->ctrlhead = head;
+
+ if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl))
+ hid_submit_ctrl(hid);
+
+ spin_unlock_irqrestore(&hid->ctrllock, flags);
+}
+
+int hid_wait_io(struct hid_device *hid)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int timeout = 10*HZ;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&hid->wait, &wait);
+
+ while (timeout && test_bit(HID_CTRL_RUNNING, &hid->iofl) &&
+ test_bit(HID_OUT_RUNNING, &hid->iofl))
+ timeout = schedule_timeout(timeout);
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hid->wait, &wait);
+
+ if (!timeout) {
+ dbg("timeout waiting for ctrl or out queue to clear");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
+ unsigned char type, void *buf, int size)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+ (type << 8), ifnum, buf, size, HZ * USB_CTRL_GET_TIMEOUT);
}
int hid_open(struct hid_device *hid)
if (hid->open++)
return 0;
- hid->urb.dev = hid->dev;
+ hid->urbin->dev = hid->dev;
- if (usb_submit_urb(&hid->urb, GFP_KERNEL))
+ if (usb_submit_urb(hid->urbin, GFP_KERNEL))
return -EIO;
return 0;
void hid_close(struct hid_device *hid)
{
if (!--hid->open)
- usb_unlink_urb(&hid->urb);
+ usb_unlink_urb(hid->urbin);
}
/*
- * Initialize all readable reports
+ * Initialize all reports
*/
+
void hid_init_reports(struct hid_device *hid)
{
- int i;
- struct hid_report *report;
struct hid_report_enum *report_enum;
+ struct hid_report *report;
struct list_head *list;
+ int len;
- for (i = 0; i < HID_REPORT_TYPES; i++) {
- if (i == HID_FEATURE_REPORT || i == HID_INPUT_REPORT) {
- report_enum = hid->report_enum + i;
- list = report_enum->report_list.next;
- while (list != &report_enum->report_list) {
- report = (struct hid_report *) list;
- hid_set_idle(hid->dev, hid->ifnum, 0, report->id);
- hid_read_report(hid, report);
- list = list->next;
- }
- }
+ report_enum = hid->report_enum + HID_INPUT_REPORT;
+ list = report_enum->report_list.next;
+ while (list != &report_enum->report_list) {
+ report = (struct hid_report *) list;
+ hid_submit_report(hid, report, USB_DIR_IN);
+ list = list->next;
+ }
+
+ report_enum = hid->report_enum + HID_FEATURE_REPORT;
+ list = report_enum->report_list.next;
+ while (list != &report_enum->report_list) {
+ report = (struct hid_report *) list;
+ hid_submit_report(hid, report, USB_DIR_IN);
+ list = list->next;
+ }
+
+ if (hid_wait_io(hid)) {
+ warn("timeout initializing reports\n");
+ return;
+ }
+
+ report_enum = hid->report_enum + HID_INPUT_REPORT;
+ list = report_enum->report_list.next;
+ while (list != &report_enum->report_list) {
+ report = (struct hid_report *) list;
+ len = ((report->size - 1) >> 3) + 1 + report_enum->numbered;
+ if (len > hid->urbin->transfer_buffer_length)
+ hid->urbin->transfer_buffer_length = len < HID_BUFFER_SIZE ? len : HID_BUFFER_SIZE;
+ usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0),
+ 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE, report->id,
+ hid->ifnum, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
+ list = list->next;
}
}
#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010
#define USB_DEVICE_ID_WACOM_INTUOS 0x0020
+#define USB_VENDOR_ID_GRIFFIN 0x077d
+#define USB_DEVICE_ID_POWERMATE 0x0410
+#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
+
struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2},
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3},
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4},
+ { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE },
+ { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB },
{ 0, 0 }
};
-static int get_class_descriptor(struct usb_device *dev, int ifnum,
- unsigned char type, void *buf, int size)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
- (type << 8), ifnum, buf, size,
- HZ * USB_CTRL_GET_TIMEOUT);
-}
-
-
static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
{
struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0;
{
__u8 rdesc[rsize];
- if ((n = get_class_descriptor(dev, interface->bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
+ if ((n = hid_get_class_descriptor(dev, interface->bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
dbg("reading report descriptor failed");
return NULL;
}
for (n = 0; n < interface->bNumEndpoints; n++) {
struct usb_endpoint_descriptor *endpoint = &interface->endpoint[n];
- int pipe, maxp;
+ int pipe;
if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */
continue;
- if (!(endpoint->bEndpointAddress & 0x80)) /* Not an input endpoint */
- continue;
-
- pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
-
- FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval);
-
- break;
+ if (endpoint->bEndpointAddress & USB_DIR_IN) {
+ if (hid->urbin)
+ continue;
+ if (!(hid->urbin = usb_alloc_urb(0)))
+ goto fail;
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ FILL_INT_URB(hid->urbin, dev, pipe, hid->inbuf, 0, hid_irq_in, hid, endpoint->bInterval);
+ } else {
+ if (hid->urbout)
+ continue;
+ if (!(hid->urbout = usb_alloc_urb(0)))
+ goto fail;
+ pipe = usb_sndbulkpipe(dev, endpoint->bEndpointAddress);
+ FILL_BULK_URB(hid->urbout, dev, pipe, hid->outbuf, 0, hid_irq_out, hid);
+ }
}
- if (n == interface->bNumEndpoints) {
- dbg("couldn't find an input interrupt endpoint");
- hid_free_device(hid);
- return NULL;
+ if (!hid->urbin) {
+ err("couldn't find an input interrupt endpoint");
+ goto fail;
}
+ init_waitqueue_head(&hid->wait);
+
+ hid->outlock = SPIN_LOCK_UNLOCKED;
+ hid->ctrllock = SPIN_LOCK_UNLOCKED;
+
hid->version = hdesc->bcdHID;
hid->country = hdesc->bCountryCode;
hid->dev = dev;
hid->ifnum = interface->bInterfaceNumber;
- for (n = 0; n < HID_CONTROL_FIFO_SIZE; n++) {
- hid->out[n].dr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- hid->out[n].dr.bRequest = HID_REQ_SET_REPORT;
- hid->out[n].dr.wIndex = cpu_to_le16(hid->ifnum);
- }
-
hid->name[0] = 0;
- if (!(buf = kmalloc(63, GFP_KERNEL)))
- return NULL;
+ if (!(buf = kmalloc(64, GFP_KERNEL)))
+ goto fail;
- if (usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) {
+ if (usb_string(dev, dev->descriptor.iManufacturer, buf, 64) > 0) {
strcat(hid->name, buf);
- if (usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+ if (usb_string(dev, dev->descriptor.iProduct, buf, 64) > 0)
sprintf(hid->name, "%s %s", hid->name, buf);
} else
sprintf(hid->name, "%04x:%04x", dev->descriptor.idVendor, dev->descriptor.idProduct);
- kfree(buf);
+ usb_make_path(dev, buf, 63);
+ sprintf(hid->phys, "%s/input%d", buf, ifnum);
- FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0),
- (void*) &hid->out[0].dr, hid->out[0].buffer, 1, hid_ctrl, hid);
+ if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
+ hid->uniq[0] = 0;
-/*
- * Some devices don't like this and crash. I don't know of any devices
- * needing this, so it is disabled for now.
- */
+ kfree(buf);
-#if 0
- if (interface->bInterfaceSubClass == 1)
- hid_set_protocol(dev, hid->ifnum, 1);
-#endif
+ hid->urbctrl = usb_alloc_urb(0);
+ FILL_CONTROL_URB(hid->urbctrl, dev, 0, (void*) &hid->cr, hid->ctrlbuf, 1, hid_ctrl, hid);
return hid;
+
+fail:
+
+ hid_free_device(hid);
+ if (hid->urbin) usb_free_urb(hid->urbin);
+ if (hid->urbout) usb_free_urb(hid->urbout);
+ if (hid->urbctrl) usb_free_urb(hid->urbctrl);
+
+ return NULL;
}
static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
{
struct hid_device *hid;
+ char path[64];
int i;
char *c;
if (!hiddev_connect(hid))
hid->claimed |= HID_CLAIMED_HIDDEV;
#endif
+
+ if (!hid->claimed) {
+ hid_free_device(hid);
+ return NULL;
+ }
+
printk(KERN_INFO);
if (hid->claimed & HID_CLAIMED_INPUT)
- printk("input%d", hid->input.number);
+ printk("input");
if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
printk(",");
if (hid->claimed & HID_CLAIMED_HIDDEV)
break;
}
- printk(": USB HID v%x.%02x %s [%s] on usb%d:%d.%d\n",
- hid->version >> 8, hid->version & 0xff, c, hid->name,
- dev->bus->busnum, dev->devnum, ifnum);
+ usb_make_path(dev, path, 63);
+
+ printk(": USB HID v%x.%02x %s [%s] on %s\n",
+ hid->version >> 8, hid->version & 0xff, c, hid->name, path);
return hid;
}
struct hid_device *hid = ptr;
dbg("cleanup called");
- usb_unlink_urb(&hid->urb);
+ usb_unlink_urb(hid->urbin);
+ usb_unlink_urb(hid->urbout);
+ usb_unlink_urb(hid->urbctrl);
+
+ usb_free_urb(hid->urbin);
+ usb_free_urb(hid->urbctrl);
+ if (hid->urbout)
+ usb_free_urb(hid->urbout);
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid);
}
static struct usb_device_id hid_usb_ids [] = {
- { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS,
- bInterfaceClass: USB_INTERFACE_CLASS_HID },
+ { bInterfaceClass: USB_INTERFACE_CLASS_HID },
{ } /* Terminating entry */
};
hiddev_init();
#endif
usb_register(&hid_driver);
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
module_init(hid_init);
module_exit(hid_exit);
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
/*
- * $Id: hid-debug.h,v 1.3 2001/05/10 15:56:07 vojtech Exp $
+ * $Id: hid-debug.h,v 1.8 2001/09/25 09:37:57 vojtech Exp $
*
* (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de>
- * (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
+ * (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
*
* Some debug stuff for the HID parser.
- *
- * Sponsored by SuSE
*/
/*
* 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
struct hid_usage_entry {
};
static struct hid_usage_entry hid_usage_table[] = {
+ { 0, 0, "Undefined" },
{ 1, 0, "GenericDesktop" },
{0, 0x01, "Pointer"},
{0, 0x02, "Mouse"},
{ 7, 0, "Keyboard" },
{ 8, 0, "LED" },
{ 9, 0, "Button" },
+ { 10, 0, "Ordinal" },
{ 12, 0, "Hotkey" },
{ 13, 0, "Digitizers" },
{0, 0x01, "Digitizer"},
{0, 0x45, "Eraser"},
{0, 0x46, "TabletPick"},
{ 15, 0, "PhysicalInterfaceDevice" },
+ {0, 0x00, "Undefined"},
+ {0, 0x01, "Physical_Interface_Device"},
+ {0, 0x20, "Normal"},
+ {0, 0x21, "Set_Effect_Report"},
+ {0, 0x22, "Effect_Block_Index"},
+ {0, 0x23, "Parameter_Block_Offset"},
+ {0, 0x24, "ROM_Flag"},
+ {0, 0x25, "Effect_Type"},
+ {0, 0x26, "ET_Constant_Force"},
+ {0, 0x27, "ET_Ramp"},
+ {0, 0x28, "ET_Custom_Force_Data"},
+ {0, 0x30, "ET_Square"},
+ {0, 0x31, "ET_Sine"},
+ {0, 0x32, "ET_Triangle"},
+ {0, 0x33, "ET_Sawtooth_Up"},
+ {0, 0x34, "ET_Sawtooth_Down"},
+ {0, 0x40, "ET_Spring"},
+ {0, 0x41, "ET_Damper"},
+ {0, 0x42, "ET_Inertia"},
+ {0, 0x43, "ET_Friction"},
+ {0, 0x50, "Duration"},
+ {0, 0x51, "Sample_Period"},
+ {0, 0x52, "Gain"},
+ {0, 0x53, "Trigger_Button"},
+ {0, 0x54, "Trigger_Repeat_Interval"},
+ {0, 0x55, "Axes_Enable"},
+ {0, 0x56, "Direction_Enable"},
+ {0, 0x57, "Direction"},
+ {0, 0x58, "Type_Specific_Block_Offset"},
+ {0, 0x59, "Block_Type"},
+ {0, 0x5A, "Set_Envelope_Report"},
+ {0, 0x5B, "Attack_Level"},
+ {0, 0x5C, "Attack_Time"},
+ {0, 0x5D, "Fade_Level"},
+ {0, 0x5E, "Fade_Time"},
+ {0, 0x5F, "Set_Condition_Report"},
+ {0, 0x60, "CP_Offset"},
+ {0, 0x61, "Positive_Coefficient"},
+ {0, 0x62, "Negative_Coefficient"},
+ {0, 0x63, "Positive_Saturation"},
+ {0, 0x64, "Negative_Saturation"},
+ {0, 0x65, "Dead_Band"},
+ {0, 0x66, "Download_Force_Sample"},
+ {0, 0x67, "Isoch_Custom_Force_Enable"},
+ {0, 0x68, "Custom_Force_Data_Report"},
+ {0, 0x69, "Custom_Force_Data"},
+ {0, 0x6A, "Custom_Force_Vendor_Defined_Data"},
+ {0, 0x6B, "Set_Custom_Force_Report"},
+ {0, 0x6C, "Custom_Force_Data_Offset"},
+ {0, 0x6D, "Sample_Count"},
+ {0, 0x6E, "Set_Periodic_Report"},
+ {0, 0x6F, "Offset"},
+ {0, 0x70, "Magnitude"},
+ {0, 0x71, "Phase"},
+ {0, 0x72, "Period"},
+ {0, 0x73, "Set_Constant_Force_Report"},
+ {0, 0x74, "Set_Ramp_Force_Report"},
+ {0, 0x75, "Ramp_Start"},
+ {0, 0x76, "Ramp_End"},
+ {0, 0x77, "Effect_Operation_Report"},
+ {0, 0x78, "Effect_Operation"},
+ {0, 0x79, "Op_Effect_Start"},
+ {0, 0x7A, "Op_Effect_Start_Solo"},
+ {0, 0x7B, "Op_Effect_Stop"},
+ {0, 0x7C, "Loop_Count"},
+ {0, 0x7D, "Device_Gain_Report"},
+ {0, 0x7E, "Device_Gain"},
+ {0, 0x7F, "PID_Pool_Report"},
+ {0, 0x80, "RAM_Pool_Size"},
+ {0, 0x81, "ROM_Pool_Size"},
+ {0, 0x82, "ROM_Effect_Block_Count"},
+ {0, 0x83, "Simultaneous_Effects_Max"},
+ {0, 0x84, "Pool_Alignment"},
+ {0, 0x85, "PID_Pool_Move_Report"},
+ {0, 0x86, "Move_Source"},
+ {0, 0x87, "Move_Destination"},
+ {0, 0x88, "Move_Length"},
+ {0, 0x89, "PID_Block_Load_Report"},
+ {0, 0x8B, "Block_Load_Status"},
+ {0, 0x8C, "Block_Load_Success"},
+ {0, 0x8D, "Block_Load_Full"},
+ {0, 0x8E, "Block_Load_Error"},
+ {0, 0x8F, "Block_Handle"},
+ {0, 0x90, "PID_Block_Free_Report"},
+ {0, 0x91, "Type_Specific_Block_Handle"},
+ {0, 0x92, "PID_State_Report"},
+ {0, 0x94, "Effect_Playing"},
+ {0, 0x95, "PID_Device_Control_Report"},
+ {0, 0x96, "PID_Device_Control"},
+ {0, 0x97, "DC_Enable_Actuators"},
+ {0, 0x98, "DC_Disable_Actuators"},
+ {0, 0x99, "DC_Stop_All_Effects"},
+ {0, 0x9A, "DC_Device_Reset"},
+ {0, 0x9B, "DC_Device_Pause"},
+ {0, 0x9C, "DC_Device_Continue"},
+ {0, 0x9F, "Device_Paused"},
+ {0, 0xA0, "Actuators_Enabled"},
+ {0, 0xA4, "Safety_Switch"},
+ {0, 0xA5, "Actuator_Override_Switch"},
+ {0, 0xA6, "Actuator_Power"},
+ {0, 0xA7, "Start_Delay"},
+ {0, 0xA8, "Parameter_Block_Size"},
+ {0, 0xA9, "Device_Managed_Pool"},
+ {0, 0xAA, "Shared_Parameter_Blocks"},
+ {0, 0xAB, "Create_New_Effect_Report"},
+ {0, 0xAC, "RAM_Pool_Available"},
{ 0, 0, NULL }
};
tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
}
if (field->unit) {
- tab(n); printk("Unit(%u)\n", field->unit);
+ char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
+ char *units[5][8] = {
+ { "None", "None", "None", "None", "None", "None", "None", "None" },
+ { "None", "Centimeter", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" },
+ { "None", "Radians", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" },
+ { "None", "Inch", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" },
+ { "None", "Degrees", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" }
+ };
+
+ int i;
+ int sys;
+ __u32 data = field->unit;
+
+ /* First nibble tells us which system we're in. */
+ sys = data & 0xf;
+ data >>= 4;
+
+ if(sys > 4) {
+ tab(n); printk("Unit(Invalid)\n");
+ }
+ else {
+ int earlier_unit = 0;
+
+ tab(n); printk("Unit(%s : ", systems[sys]);
+
+ for (i=1 ; i<sizeof(__u32)*2 ; i++) {
+ char nibble = data & 0xf;
+ data >>= 4;
+ if (nibble != 0) {
+ if(earlier_unit++ > 0)
+ printk("*");
+ printk("%s", units[sys][i]);
+ if(nibble != 1) {
+ /* This is a _signed_ nibble(!) */
+
+ int val = nibble & 0x7;
+ if(nibble & 0x08)
+ val = -((0x7 & ~val) +1);
+ printk("^%d", val);
+ }
+ }
+ }
+ printk(")\n");
+ }
}
tab(n); printk("Report Size(%u)\n", field->report_size);
tab(n); printk("Report Count(%u)\n", field->report_count);
/*
- * $Id: hid-input.c,v 1.5 2001/05/23 09:25:02 vojtech Exp $
+ * $Id: hid-input.c,v 1.18 2001/11/07 09:01:18 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
- * USB HID to Linux Input mapping module
- *
- * Sponsored by SuSE
+ * USB HID to Linux Input mapping
*/
/*
* 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/usb.h>
static struct {
__s32 x;
__s32 y;
-} hid_hat_to_axis[] = {{0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
static void hidinput_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage)
{
struct input_dev *input = &device->input;
int max;
+ int is_abs = 0;
unsigned long *bit;
switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */
+ set_bit(EV_REP, input->evbit);
switch (usage->hid & HID_USAGE) {
case 0x000: usage->code = 0; break;
case 0x034: usage->code = KEY_SLEEP; break;
case 0x08a: usage->code = KEY_WWW; break;
case 0x095: usage->code = KEY_HELP; break;
+ case 0x0b0: usage->code = KEY_PLAY; break;
+ case 0x0b1: usage->code = KEY_PAUSE; break;
+ case 0x0b2: usage->code = KEY_RECORD; break;
+ case 0x0b3: usage->code = KEY_FASTFORWARD; break;
case 0x0b4: usage->code = KEY_REWIND; break;
case 0x0b5: usage->code = KEY_NEXTSONG; break;
case 0x0b6: usage->code = KEY_PREVIOUSSONG; break;
case 0x0b7: usage->code = KEY_STOPCD; break;
case 0x0b8: usage->code = KEY_EJECTCD; break;
case 0x0cd: usage->code = KEY_PLAYPAUSE; break;
-
+ case 0x0e0: is_abs = 1;
+ usage->code = ABS_VOLUME;
+ break;
case 0x0e2: usage->code = KEY_MUTE; break;
+ case 0x0e5: usage->code = KEY_BASSBOOST; break;
case 0x0e9: usage->code = KEY_VOLUMEUP; break;
case 0x0ea: usage->code = KEY_VOLUMEDOWN; break;
case 0x18a: usage->code = KEY_MAIL; break;
case 0x192: usage->code = KEY_CALC; break;
case 0x194: usage->code = KEY_FILE; break;
-
case 0x21a: usage->code = KEY_UNDO; break;
case 0x21b: usage->code = KEY_COPY; break;
case 0x21c: usage->code = KEY_CUT; break;
case 0x227: usage->code = KEY_REFRESH; break;
case 0x22a: usage->code = KEY_BOOKMARKS; break;
+ default: usage->code = KEY_UNKNOWN; break;
+ }
+
+ if (is_abs) {
+ usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
+ } else {
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ }
+ break;
+
+ case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */
+
+ set_bit(EV_REP, input->evbit);
+ switch (usage->hid & HID_USAGE) {
+ case 0x021: usage->code = KEY_PRINT; break;
+ case 0x070: usage->code = KEY_HP; break;
+ case 0x071: usage->code = KEY_CAMERA; break;
+ case 0x072: usage->code = KEY_SOUND; break;
+ case 0x073: usage->code = KEY_QUESTION; break;
+
+ case 0x080: usage->code = KEY_EMAIL; break;
+ case 0x081: usage->code = KEY_CHAT; break;
+ case 0x082: usage->code = KEY_SEARCH; break;
+ case 0x083: usage->code = KEY_CONNECT; break;
+ case 0x084: usage->code = KEY_FINANCE; break;
+ case 0x085: usage->code = KEY_SPORT; break;
+ case 0x086: usage->code = KEY_SHOP; break;
+
default: usage->code = KEY_UNKNOWN; break;
}
}
hid_set_field(field, offset, value);
- hid_write_report(hid, field->report);
+ hid_submit_report(hid, field->report, USB_DIR_OUT);
return 0;
}
hid->input.close = hidinput_close;
hid->input.name = hid->name;
+ hid->input.phys = hid->phys;
+ hid->input.uniq = hid->uniq;
hid->input.idbus = BUS_USB;
hid->input.idvendor = dev->descriptor.idVendor;
hid->input.idproduct = dev->descriptor.idProduct;
#define __HID_H
/*
- * $Id: hid.h,v 1.10 2001/05/10 15:56:07 vojtech Exp $
+ * $Id: hid.h,v 1.24 2001/12/27 10:37:41 vojtech Exp $
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik
- *
- * Sponsored by SuSE
*/
/*
* 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+/*
+ * USB HID (Human Interface Device) interface class code
+ */
+
+#define USB_INTERFACE_CLASS_HID 3
+
/*
* HID class requests
*/
+
#define HID_REQ_GET_REPORT 0x01
#define HID_REQ_GET_IDLE 0x02
#define HID_REQ_GET_PROTOCOL 0x03
/*
* HID class descriptor types
*/
+
#define HID_DT_HID (USB_TYPE_CLASS | 0x01)
#define HID_DT_REPORT (USB_TYPE_CLASS | 0x02)
#define HID_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
-/*
- * Utilities for class control messaging
- */
-static inline int
-hid_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (duration << 8) | report_id, ifnum, NULL, 0,
- HZ * USB_CTRL_SET_TIMEOUT);
-}
-
-static inline int
-hid_get_protocol(struct usb_device *dev, int ifnum)
-{
- unsigned char type;
- int ret;
-
- if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- HID_REQ_GET_PROTOCOL,
- USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, ifnum, &type, 1,
- HZ * USB_CTRL_GET_TIMEOUT)) < 0)
- return ret;
-
- return type;
-}
-
-static inline int
-hid_set_protocol(struct usb_device *dev, int ifnum, int protocol)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- HID_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- protocol, ifnum, NULL, 0,
- HZ * USB_CTRL_SET_TIMEOUT);
-}
-
-static inline int
-hid_get_report(struct usb_device *dev, int ifnum, unsigned char type,
- unsigned char id, void *buf, int size)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- HID_REQ_GET_REPORT,
- USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, ifnum, buf, size,
- HZ * USB_CTRL_GET_TIMEOUT);
-}
-
-static inline int
-hid_set_report(struct usb_device *dev, int ifnum, unsigned char type,
- unsigned char id, void *buf, int size)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, ifnum, buf, size, HZ);
- // FIXME USB_CTRL_SET_TIMEOUT
-}
-
-
-/*
- * "Boot Protocol" keyboard/mouse drivers use don't use all of HID;
- * they're a lot smaller but can't support all the device features.
- */
-#ifndef _HID_BOOT_PROTOCOL
-
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-
-/*
- * USB HID (Human Interface Device) interface class code
- */
-
-#define USB_INTERFACE_CLASS_HID 3
-
/*
* We parse each description item into this structure. Short items data
* values are expanded to 32-bit signed int, long items contain a pointer
#define HID_UP_KEYBOARD 0x00070000
#define HID_UP_LED 0x00080000
#define HID_UP_BUTTON 0x00090000
+#define HID_UP_ORDINAL 0x000a0000
#define HID_UP_CONSUMER 0x000c0000
#define HID_UP_DIGITIZER 0x000d0000
#define HID_UP_PID 0x000f0000
+#define HID_UP_HPVENDOR 0xff7f0000
#define HID_USAGE 0x0000ffff
__s32 logical_maximum;
__s32 physical_minimum;
__s32 physical_maximum;
- unsigned unit_exponent;
+ __s32 unit_exponent;
unsigned unit;
unsigned report_id;
unsigned report_size;
__s32 logical_maximum;
__s32 physical_minimum;
__s32 physical_maximum;
- unsigned unit_exponent;
+ __s32 unit_exponent;
unsigned unit;
struct hid_report *report; /* associated report */
};
struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */
unsigned maxfield; /* maximum valid field index */
unsigned size; /* size of the report (bits) */
- unsigned idx; /* where we're in data */
- unsigned char *data; /* data for multi-packet reports */
struct hid_device *device; /* associated device */
};
#define HID_REPORT_TYPES 3
#define HID_BUFFER_SIZE 32
-#define HID_CONTROL_FIFO_SIZE 8
+#define HID_CONTROL_FIFO_SIZE 64
+#define HID_OUTPUT_FIFO_SIZE 64
struct hid_control_fifo {
- struct usb_ctrlrequest dr;
- char buffer[HID_BUFFER_SIZE];
+ unsigned char dir;
+ struct hid_report *report;
};
#define HID_CLAIMED_INPUT 1
#define HID_CLAIMED_HIDDEV 2
+#define HID_CTRL_RUNNING 1
+#define HID_OUT_RUNNING 2
+
struct hid_device { /* device report descriptor */
__u8 *rdesc;
unsigned rsize;
struct usb_device *dev; /* USB device */
int ifnum; /* USB interface number */
- struct urb urb; /* USB URB structure */
- char buffer[HID_BUFFER_SIZE]; /* Rx buffer */
+ unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
+
+ struct urb *urbin; /* Input URB */
+ char inbuf[HID_BUFFER_SIZE]; /* Input buffer */
- struct urb urbout; /* Output URB */
- struct hid_control_fifo out[HID_CONTROL_FIFO_SIZE]; /* Transmit buffer */
- unsigned char outhead, outtail; /* Tx buffer head & tail */
+ struct urb *urbctrl; /* Control URB */
+ struct usb_ctrlrequest cr; /* Control request struct */
+ struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
+ unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
+ char ctrlbuf[HID_BUFFER_SIZE]; /* Control buffer */
+ spinlock_t ctrllock; /* Control fifo spinlock */
+
+ struct urb *urbout; /* Output URB */
+ struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
+ unsigned char outhead, outtail; /* Output pipe fifo head & tail */
+ char outbuf[HID_BUFFER_SIZE]; /* Output buffer */
+ spinlock_t outlock; /* Output fifo spinlock */
unsigned claimed; /* Claimed by hidinput, hiddev? */
unsigned quirks; /* Various quirks the device can pull on us */
void *hiddev; /* The hiddev structure */
int minor; /* Hiddev minor number */
+ wait_queue_head_t wait; /* For sleeping */
+
int open; /* is the device open by anyone? */
char name[128]; /* Device name */
+ char phys[64]; /* Device physical location */
+ char uniq[64]; /* Device unique identifier (serial #) */
};
#define HID_GLOBAL_STACK_SIZE 4
#else
#define hid_dump_input(a,b) do { } while (0)
#define hid_dump_device(c) do { } while (0)
-#endif /* DEBUG */
+#define hid_dump_field(a,b) do { } while (0)
+#endif
-#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || ( a == 0x000c0001))
+#endif
+
+/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
+/* We ignore a few input applications that are not widely used */
+#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || ( a == 0x00010080) || ( a == 0x000c0001))
int hid_open(struct hid_device *);
void hid_close(struct hid_device *);
int hid_find_field(struct hid_device *, unsigned int, unsigned int, struct hid_field **);
int hid_set_field(struct hid_field *, unsigned, __s32);
-void hid_write_report(struct hid_device *, struct hid_report *);
-void hid_read_report(struct hid_device *, struct hid_report *);
+void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
void hid_init_reports(struct hid_device *hid);
-
-#endif /* !_HID_BOOT_PROTOCOL */
-
-#endif /* !__HID_H */
-
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
- hid_read_report(hid, report);
+ hid_submit_report(hid, report, USB_DIR_IN);
return 0;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
- hid_write_report(hid, report);
+ hid_submit_report(hid, report, USB_DIR_OUT);
return 0;
/*
- * $Id: usbkbd.c,v 1.20 2001/04/26 08:34:49 vojtech Exp $
+ * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* USB HIDBP Keyboard support
- *
- * Sponsored by SuSE
*/
/*
* 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/usb.h>
-#define _HID_BOOT_PROTOCOL
-#include "hid.h"
-
/*
* Version Information
*/
#define DRIVER_VERSION ""
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@suse.cz>"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
+#define DRIVER_LICENSE "GPL"
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
static unsigned char usb_kbd_keycode[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
unsigned char new[8];
unsigned char old[8];
struct urb *irq, *led;
- struct usb_ctrlrequest dr;
+ struct usb_ctrlrequest cr;
unsigned char leds, newleds;
char name[128];
+ char phys[64];
int open;
};
kbd->leds = kbd->newleds;
kbd->led->dev = kbd->usbdev;
- if (usb_submit_urb(kbd->led, GFP_KERNEL))
+ if (usb_submit_urb(kbd->led, GFP_ATOMIC))
err("usb_submit_urb(leds) failed");
return 0;
kbd->leds = kbd->newleds;
kbd->led->dev = kbd->usbdev;
- if (usb_submit_urb(kbd->led, GFP_KERNEL))
+ if (usb_submit_urb(kbd->led, GFP_ATOMIC))
err("usb_submit_urb(leds) failed");
}
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
int i, pipe, maxp;
+ char path[64];
char *buf;
iface = &dev->actconfig->interface[ifnum];
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
- hid_set_protocol(dev, interface->bInterfaceNumber, 0);
- hid_set_idle(dev, interface->bInterfaceNumber, 0, 0);
-
if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL))) return NULL;
memset(kbd, 0, sizeof(struct usb_kbd));
FILL_INT_URB(kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp,
usb_kbd_irq, kbd, endpoint->bInterval);
- kbd->dr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- kbd->dr.bRequest = HID_REQ_SET_REPORT;
- kbd->dr.wValue = 0x200;
- kbd->dr.wIndex = interface->bInterfaceNumber;
- kbd->dr.wLength = 1;
+ kbd->cr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ kbd->cr.bRequest = 0x09;
+ kbd->cr.wValue = 0x200;
+ kbd->cr.wIndex = interface->bInterfaceNumber;
+ kbd->cr.wLength = 1;
+
+ usb_make_path(dev, path, 64);
+ sprintf(kbd->phys, "%s/input0", path);
kbd->dev.name = kbd->name;
+ kbd->dev.phys = kbd->phys;
kbd->dev.idbus = BUS_USB;
kbd->dev.idvendor = dev->descriptor.idVendor;
kbd->dev.idproduct = dev->descriptor.idProduct;
kfree(buf);
FILL_CONTROL_URB(kbd->led, dev, usb_sndctrlpipe(dev, 0),
- (void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd);
+ (void*) &kbd->cr, &kbd->leds, 1, usb_kbd_led, kbd);
input_register_device(&kbd->dev);
- printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
- kbd->dev.number, kbd->name, dev->bus->busnum, dev->devnum, ifnum);
+ printk(KERN_INFO "input: %s on %s\n", kbd->name, path);
return kbd;
}
/*
- * $Id: usbmouse.c,v 1.6 2000/08/14 21:05:26 vojtech Exp $
+ * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $
*
- * Copyright (c) 1999-2000 Vojtech Pavlik
+ * Copyright (c) 1999-2001 Vojtech Pavlik
*
* USB HIDBP Mouse support
- *
- * Sponsored by SuSE
*/
/*
* 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/usb.h>
-#define _HID_BOOT_PROTOCOL
-#include "hid.h"
-
/*
* Version Information
*/
#define DRIVER_VERSION "v1.6"
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@suse.cz>"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
+#define DRIVER_LICENSE "GPL"
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
struct usb_mouse {
signed char data[8];
char name[128];
+ char phys[64];
struct usb_device *usbdev;
struct input_dev dev;
struct urb *irq;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
int pipe, maxp;
+ char path[64];
char *buf;
iface = &dev->actconfig->interface[ifnum];
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
- hid_set_idle(dev, interface->bInterfaceNumber, 0, 0);
-
if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL;
memset(mouse, 0, sizeof(struct usb_mouse));
mouse->dev.open = usb_mouse_open;
mouse->dev.close = usb_mouse_close;
+ usb_make_path(dev, path, 64);
+ sprintf(mouse->phys, "%s/input0", path);
+
mouse->dev.name = mouse->name;
+ mouse->dev.phys = mouse->phys;
mouse->dev.idbus = BUS_USB;
mouse->dev.idvendor = dev->descriptor.idVendor;
mouse->dev.idproduct = dev->descriptor.idProduct;
input_register_device(&mouse->dev);
- printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
- mouse->dev.number, mouse->name, dev->bus->busnum, dev->devnum, ifnum);
+ printk(KERN_INFO "input: %s on %s\n", mouse->name, path);
return mouse;
}
/*
- * $Id: wacom.c,v 1.22 2001/04/26 11:26:09 vojtech Exp $
+ * $Id: wacom.c,v 1.28 2001/09/25 10:12:07 vojtech Exp $
*
- * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
* Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
* Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
*
* USB Wacom Graphire and Wacom Intuos tablet support
*
- * Sponsored by SuSE
- *
* ChangeLog:
* v0.1 (vp) - Initial release
* v0.2 (aba) - Support for all buttons / combinations
* 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 <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
* Version Information
*/
#define DRIVER_VERSION "v1.21"
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@suse.cz>"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
+#define DRIVER_LICENSE "GPL"
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
#define USB_VENDOR_ID_WACOM 0x056a
int open;
int x, y;
__u32 serial[2];
+ char phys[32];
};
static void wacom_pl_irq(struct urb *urb)
{
struct usb_endpoint_descriptor *endpoint;
struct wacom *wacom;
+ char path[64];
if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL;
memset(wacom, 0, sizeof(struct wacom));
wacom->dev.open = wacom_open;
wacom->dev.close = wacom_close;
+ usb_make_path(dev, path, 64);
+ sprintf(wacom->phys, "%s/input0", path);
+
wacom->dev.name = wacom->features->name;
+ wacom->dev.phys = wacom->phys;
wacom->dev.idbus = BUS_USB;
wacom->dev.idvendor = dev->descriptor.idVendor;
wacom->dev.idproduct = dev->descriptor.idProduct;
input_register_device(&wacom->dev);
- printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
- wacom->dev.number, wacom->features->name, dev->bus->busnum, dev->devnum, ifnum);
+ printk(KERN_INFO "input: %s on %s\n", wacom->features->name, path);
return wacom;
}