From 7fd88a74dd552b0096c543e413327fbfb350ef80 Mon Sep 17 00:00:00 2001 From: Joshua Stein Date: Fri, 8 Jan 2016 15:54:15 +0000 Subject: Move HID support files out of dev/usb into new dev/hid directory These files aren't USB-specific and were used by the previous Bluetooth implementation, and will be used by the upcoming HID-over-i2C implementation ok deraadt previous version ok kettenis and mpi --- sys/dev/hid/files.hid | 18 ++ sys/dev/hid/hid.c | 639 +++++++++++++++++++++++++++++++++++++++++++++++ sys/dev/hid/hid.h | 404 ++++++++++++++++++++++++++++++ sys/dev/hid/hidkbd.c | 642 ++++++++++++++++++++++++++++++++++++++++++++++++ sys/dev/hid/hidkbdsc.h | 105 ++++++++ sys/dev/hid/hidkbdvar.h | 34 +++ sys/dev/hid/hidms.c | 478 +++++++++++++++++++++++++++++++++++ sys/dev/hid/hidmsvar.h | 83 +++++++ 8 files changed, 2403 insertions(+) create mode 100644 sys/dev/hid/files.hid create mode 100644 sys/dev/hid/hid.c create mode 100644 sys/dev/hid/hid.h create mode 100644 sys/dev/hid/hidkbd.c create mode 100644 sys/dev/hid/hidkbdsc.h create mode 100644 sys/dev/hid/hidkbdvar.h create mode 100644 sys/dev/hid/hidms.c create mode 100644 sys/dev/hid/hidmsvar.h (limited to 'sys/dev/hid') diff --git a/sys/dev/hid/files.hid b/sys/dev/hid/files.hid new file mode 100644 index 00000000000..c2bd1d2ce80 --- /dev/null +++ b/sys/dev/hid/files.hid @@ -0,0 +1,18 @@ +# $OpenBSD: files.hid,v 1.1 2016/01/08 15:54:13 jcs Exp $ + +# Human Interface Devices + +# HID "bus" +define hidbus {[reportid = -1]} + +# HID processing +define hid +file dev/hid/hid.c hid + +# Keyboards +define hidkbd +file dev/hid/hidkbd.c hidkbd needs-flag + +# Mice +define hidms +file dev/hid/hidms.c hidms diff --git a/sys/dev/hid/hid.c b/sys/dev/hid/hid.c new file mode 100644 index 00000000000..94a9bcf01f0 --- /dev/null +++ b/sys/dev/hid/hid.c @@ -0,0 +1,639 @@ +/* $OpenBSD: hid.c,v 1.1 2016/01/08 15:54:13 jcs Exp $ */ +/* $NetBSD: hid.c,v 1.23 2002/07/11 21:14:25 augustss Exp $ */ +/* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#ifdef USBHID_DEBUG +#define DPRINTF(x...) do { printf(x); } while (0) +#else +#define DPRINTF(x...) +#endif + +#define MAXUSAGE 64 +#define MAXPUSH 4 +#define MAXID 16 + +struct hid_pos_data { + int32_t rid; + uint32_t pos; +}; + +struct hid_data { + const uint8_t *start; + const uint8_t *end; + const uint8_t *p; + struct hid_item cur[MAXPUSH]; + struct hid_pos_data last_pos[MAXID]; + int32_t usages_min[MAXUSAGE]; + int32_t usages_max[MAXUSAGE]; + int32_t usage_last; /* last seen usage */ + uint32_t loc_size; /* last seen size */ + uint32_t loc_count; /* last seen count */ + enum hid_kind kind; + uint8_t pushlevel; /* current pushlevel */ + uint8_t ncount; /* end usage item count */ + uint8_t icount; /* current usage item count */ + uint8_t nusage; /* end "usages_min/max" index */ + uint8_t iusage; /* current "usages_min/max" index */ + uint8_t ousage; /* current "usages_min/max" offset */ + uint8_t susage; /* usage set flags */ +}; + +static void +hid_clear_local(struct hid_item *c) +{ + c->loc.count = 0; + c->loc.size = 0; + c->usage = 0; + c->usage_minimum = 0; + c->usage_maximum = 0; + c->designator_index = 0; + c->designator_minimum = 0; + c->designator_maximum = 0; + c->string_index = 0; + c->string_minimum = 0; + c->string_maximum = 0; + c->set_delimiter = 0; +} + +static void +hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t nextid) +{ + uint8_t i; + + if (c->report_ID == nextid) + return; + + /* save current position for current rID */ + if (c->report_ID == 0) { + i = 0; + } else { + for (i = 1; i != MAXID; i++) { + if (s->last_pos[i].rid == c->report_ID) + break; + if (s->last_pos[i].rid == 0) + break; + } + } + if (i != MAXID) { + s->last_pos[i].rid = c->report_ID; + s->last_pos[i].pos = c->loc.pos; + } + + /* store next report ID */ + c->report_ID = nextid; + + /* lookup last position for next rID */ + if (nextid == 0) { + i = 0; + } else { + for (i = 1; i != MAXID; i++) { + if (s->last_pos[i].rid == nextid) + break; + if (s->last_pos[i].rid == 0) + break; + } + } + if (i != MAXID) { + s->last_pos[i].rid = nextid; + c->loc.pos = s->last_pos[i].pos; + } else { + DPRINTF("Out of RID entries, position is set to zero!\n"); + c->loc.pos = 0; + } +} + +struct hid_data * +hid_start_parse(const void *d, int len, enum hid_kind kind) +{ + struct hid_data *s; + + s = malloc(sizeof(*s), M_TEMP, M_WAITOK | M_ZERO); + + s->start = s->p = d; + s->end = ((const uint8_t *)d) + len; + s->kind = kind; + return (s); +} + +void +hid_end_parse(struct hid_data *s) +{ + if (s == NULL) + return; + + free(s, M_TEMP, 0); +} + +static uint8_t +hid_get_byte(struct hid_data *s, const uint16_t wSize) +{ + const uint8_t *ptr; + uint8_t retval; + + ptr = s->p; + + /* check if end is reached */ + if (ptr == s->end) + return (0); + + /* read out a byte */ + retval = *ptr; + + /* check if data pointer can be advanced by "wSize" bytes */ + if ((s->end - ptr) < wSize) + ptr = s->end; + else + ptr += wSize; + + /* update pointer */ + s->p = ptr; + + return (retval); +} + +int +hid_get_item(struct hid_data *s, struct hid_item *h) +{ + struct hid_item *c; + unsigned int bTag, bType, bSize; + uint32_t oldpos; + int32_t mask; + int32_t dval; + + if (s == NULL) + return (0); + + c = &s->cur[s->pushlevel]; + + top: + /* check if there is an array of items */ + DPRINTF("%s: icount=%d ncount=%d\n", __func__, + s->icount, s->ncount); + if (s->icount < s->ncount) { + /* get current usage */ + if (s->iusage < s->nusage) { + dval = s->usages_min[s->iusage] + s->ousage; + c->usage = dval; + s->usage_last = dval; + if (dval == s->usages_max[s->iusage]) { + s->iusage ++; + s->ousage = 0; + } else { + s->ousage ++; + } + } else { + DPRINTF("Using last usage\n"); + dval = s->usage_last; + } + s->icount ++; + /* + * Only copy HID item, increment position and return + * if correct kind! + */ + if (s->kind == c->kind) { + *h = *c; + DPRINTF("%u,%u,%u\n", h->loc.pos, + h->loc.size, h->loc.count); + c->loc.pos += c->loc.size * c->loc.count; + return (1); + } + } + + /* reset state variables */ + s->icount = 0; + s->ncount = 0; + s->iusage = 0; + s->nusage = 0; + s->susage = 0; + s->ousage = 0; + hid_clear_local(c); + + /* get next item */ + while (s->p != s->end) { + + bSize = hid_get_byte(s, 1); + if (bSize == 0xfe) { + /* long item */ + bSize = hid_get_byte(s, 1); + bSize |= hid_get_byte(s, 1) << 8; + bTag = hid_get_byte(s, 1); + bType = 0xff; /* XXX what should it be */ + } else { + /* short item */ + bTag = bSize >> 4; + bType = (bSize >> 2) & 3; + bSize &= 3; + if (bSize == 3) + bSize = 4; + } + switch (bSize) { + case 0: + dval = 0; + mask = 0; + break; + case 1: + dval = hid_get_byte(s, 1); + mask = 0xFF; + break; + case 2: + dval = hid_get_byte(s, 1); + dval |= hid_get_byte(s, 1) << 8; + mask = 0xFFFF; + break; + case 4: + dval = hid_get_byte(s, 1); + dval |= hid_get_byte(s, 1) << 8; + dval |= hid_get_byte(s, 1) << 16; + dval |= hid_get_byte(s, 1) << 24; + mask = 0xFFFFFFFF; + break; + default: + dval = hid_get_byte(s, bSize); + DPRINTF("bad length %u (data=0x%02x)\n", + bSize, dval); + continue; + } + + DPRINTF("%s: bType=%d bTag=%d dval=%d\n", __func__, + bType, bTag, dval); + switch (bType) { + case 0: /* Main */ + switch (bTag) { + case 8: /* Input */ + c->kind = hid_input; + c->flags = dval; + ret: + c->loc.count = s->loc_count; + c->loc.size = s->loc_size; + + if (c->flags & HIO_VARIABLE) { + /* range check usage count */ + if (c->loc.count > 255) { + DPRINTF("Number of " + "items truncated to 255\n"); + s->ncount = 255; + } else + s->ncount = c->loc.count; + + /* + * The "top" loop will return + * one and one item: + */ + c->loc.count = 1; + } else { + s->ncount = 1; + } + goto top; + + case 9: /* Output */ + c->kind = hid_output; + c->flags = dval; + goto ret; + case 10: /* Collection */ + c->kind = hid_collection; + c->collection = dval; + c->collevel++; + c->usage = s->usage_last; + *h = *c; + return (1); + case 11: /* Feature */ + c->kind = hid_feature; + c->flags = dval; + goto ret; + case 12: /* End collection */ + c->kind = hid_endcollection; + if (c->collevel == 0) { + DPRINTF("invalid end collection\n"); + return (0); + } + c->collevel--; + *h = *c; + return (1); + default: + DPRINTF("Main bTag=%d\n", bTag); + break; + } + break; + case 1: /* Global */ + switch (bTag) { + case 0: + c->_usage_page = dval << 16; + break; + case 1: + c->logical_minimum = dval; + break; + case 2: + c->logical_maximum = dval; + break; + case 3: + c->physical_minimum = dval; + break; + case 4: + c->physical_maximum = dval; + break; + case 5: + c->unit_exponent = dval; + break; + case 6: + c->unit = dval; + break; + case 7: + /* mask because value is unsigned */ + s->loc_size = dval & mask; + break; + case 8: + hid_switch_rid(s, c, dval & mask); + break; + case 9: + /* mask because value is unsigned */ + s->loc_count = dval & mask; + break; + case 10: /* Push */ + s->pushlevel ++; + if (s->pushlevel < MAXPUSH) { + s->cur[s->pushlevel] = *c; + /* store size and count */ + c->loc.size = s->loc_size; + c->loc.count = s->loc_count; + /* update current item pointer */ + c = &s->cur[s->pushlevel]; + } else { + DPRINTF("Cannot push " + "item @ %d\n", s->pushlevel); + } + break; + case 11: /* Pop */ + s->pushlevel --; + if (s->pushlevel < MAXPUSH) { + /* preserve position */ + oldpos = c->loc.pos; + c = &s->cur[s->pushlevel]; + /* restore size and count */ + s->loc_size = c->loc.size; + s->loc_count = c->loc.count; + /* set default item location */ + c->loc.pos = oldpos; + c->loc.size = 0; + c->loc.count = 0; + } else { + DPRINTF("Cannot pop " + "item @ %d\n", s->pushlevel); + } + break; + default: + DPRINTF("Global bTag=%d\n", bTag); + break; + } + break; + case 2: /* Local */ + switch (bTag) { + case 0: + if (bSize != 4) + dval = (dval & mask) | c->_usage_page; + + /* set last usage, in case of a collection */ + s->usage_last = dval; + + if (s->nusage < MAXUSAGE) { + s->usages_min[s->nusage] = dval; + s->usages_max[s->nusage] = dval; + s->nusage ++; + } else { + DPRINTF("max usage reached\n"); + } + + /* clear any pending usage sets */ + s->susage = 0; + break; + case 1: + s->susage |= 1; + + if (bSize != 4) + dval = (dval & mask) | c->_usage_page; + c->usage_minimum = dval; + + goto check_set; + case 2: + s->susage |= 2; + + if (bSize != 4) + dval = (dval & mask) | c->_usage_page; + c->usage_maximum = dval; + + check_set: + if (s->susage != 3) + break; + + /* sanity check */ + if ((s->nusage < MAXUSAGE) && + (c->usage_minimum <= c->usage_maximum)) { + /* add usage range */ + s->usages_min[s->nusage] = + c->usage_minimum; + s->usages_max[s->nusage] = + c->usage_maximum; + s->nusage ++; + } else { + DPRINTF("Usage set dropped\n"); + } + s->susage = 0; + break; + case 3: + c->designator_index = dval; + break; + case 4: + c->designator_minimum = dval; + break; + case 5: + c->designator_maximum = dval; + break; + case 7: + c->string_index = dval; + break; + case 8: + c->string_minimum = dval; + break; + case 9: + c->string_maximum = dval; + break; + case 10: + c->set_delimiter = dval; + break; + default: + DPRINTF("Local bTag=%d\n", bTag); + break; + } + break; + default: + DPRINTF("default bType=%d\n", bType); + break; + } + } + return (0); +} + +int +hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t id) +{ + struct hid_data *d; + struct hid_item h; + int lo, hi; + + h.report_ID = 0; + lo = hi = -1; + DPRINTF("hid_report_size: kind=%d id=%d\n", k, id); + for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) { + DPRINTF("hid_report_size: item kind=%d id=%d pos=%d " + "size=%d count=%d\n", + h.kind, h.report_ID, h.loc.pos, h.loc.size, + h.loc.count); + if (h.report_ID == id && h.kind == k) { + if (lo < 0) { + lo = h.loc.pos; +#ifdef DIAGNOSTIC + if (lo != 0) { + printf("hid_report_size: lo != 0\n"); + } +#endif + } + hi = h.loc.pos + h.loc.size * h.loc.count; + DPRINTF("hid_report_size: lo=%d hi=%d\n", lo, hi); + + } + } + hid_end_parse(d); + return ((hi - lo + 7) / 8); +} + +int +hid_locate(const void *desc, int size, int32_t u, uint8_t id, enum hid_kind k, + struct hid_location *loc, uint32_t *flags) +{ + struct hid_data *d; + struct hid_item h; + + h.report_ID = 0; + DPRINTF("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id); + for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) { + DPRINTF("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n", + h.usage, h.kind, h.report_ID, h.flags); + if (h.kind == k && !(h.flags & HIO_CONST) && + h.usage == u && h.report_ID == id) { + if (loc != NULL) + *loc = h.loc; + if (flags != NULL) + *flags = h.flags; + hid_end_parse(d); + return (1); + } + } + hid_end_parse(d); + if (loc != NULL) + loc->size = 0; + if (flags != NULL) + *flags = 0; + return (0); +} + +int32_t +hid_get_data(const uint8_t *buf, int len, struct hid_location *loc) +{ + uint32_t hpos = loc->pos; + uint32_t hsize = loc->size; + uint32_t data; + uint32_t rpos; + uint8_t n; + + DPRINTF("hid_get_data: loc %d/%d\n", hpos, hsize); + + /* Range check and limit */ + if (hsize == 0) + return (0); + if (hsize > 32) + hsize = 32; + + /* Get data in a safe way */ + data = 0; + rpos = (hpos / 8); + n = (hsize + 7) / 8; + rpos += n; + while (n--) { + rpos--; + if (rpos < len) + data |= buf[rpos] << (8 * n); + } + + /* Correctly shift down data */ + data = (data >> (hpos % 8)); + n = 32 - hsize; + + data = (int32_t)((int32_t)data << n) >> n; + + DPRINTF("hid_get_data: loc %d/%d = %lu\n", + loc->pos, loc->size, (long)data); + return (data); +} + +int +hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage) +{ + struct hid_data *hd; + struct hid_item hi; + uint32_t coll_usage = ~0; + + hd = hid_start_parse(desc, size, hid_none); + + DPRINTF("%s: id=%d usage=0x%x\n", __func__, id, usage); + while (hid_get_item(hd, &hi)) { + DPRINTF("%s: kind=%d id=%d usage=0x%x(0x%x)\n", __func__, + hi.kind, hi.report_ID, hi.usage, coll_usage); + if (hi.kind == hid_collection && + hi.collection == HCOLL_APPLICATION) + coll_usage = hi.usage; + if (hi.kind == hid_endcollection && + coll_usage == usage && hi.report_ID == id) { + DPRINTF("%s: found\n", __func__); + hid_end_parse(hd); + return (1); + } + } + DPRINTF("%s: not found\n", __func__); + hid_end_parse(hd); + return (0); +} diff --git a/sys/dev/hid/hid.h b/sys/dev/hid/hid.h new file mode 100644 index 00000000000..53af2bae669 --- /dev/null +++ b/sys/dev/hid/hid.h @@ -0,0 +1,404 @@ +/* $OpenBSD: hid.h,v 1.1 2016/01/08 15:54:13 jcs Exp $ */ +/* $NetBSD: hid.h,v 1.8 2002/07/11 21:14:25 augustss Exp $ */ +/* $FreeBSD: src/sys/dev/usb/hid.h,v 1.7 1999/11/17 22:33:40 n_hibma Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _HIDHID_H_ +#define _HIDHID_H_ + +enum hid_kind { + hid_input, + hid_output, + hid_feature, + hid_collection, + hid_endcollection, + hid_none +}; + +struct hid_location { + u_int32_t size; + u_int32_t count; + u_int32_t pos; +}; + +struct hid_item { + /* Global */ + int32_t _usage_page; + int32_t logical_minimum; + int32_t logical_maximum; + int32_t physical_minimum; + int32_t physical_maximum; + int32_t unit_exponent; + int32_t unit; + int32_t report_ID; + /* Local */ + int32_t usage; + int32_t usage_minimum; + int32_t usage_maximum; + int32_t designator_index; + int32_t designator_minimum; + int32_t designator_maximum; + int32_t string_index; + int32_t string_minimum; + int32_t string_maximum; + int32_t set_delimiter; + /* Misc */ + int32_t collection; + int collevel; + enum hid_kind kind; + u_int32_t flags; + /* Location */ + struct hid_location loc; + /* */ + struct hid_item *next; +}; + +struct hid_data *hid_start_parse(const void *, int, enum hid_kind); +void hid_end_parse(struct hid_data *); +int hid_get_item(struct hid_data *, struct hid_item *); +int hid_report_size(const void *, int, enum hid_kind, uint8_t); +int hid_locate(const void *, int, int32_t, uint8_t, enum hid_kind, + struct hid_location *, uint32_t *); +int32_t hid_get_data(const uint8_t *buf, int, struct hid_location *); +int hid_is_collection(const void *, int, uint8_t, int32_t); + +/* Usage pages */ +#define HUP_UNDEFINED 0x0000 +#define HUP_GENERIC_DESKTOP 0x0001 +#define HUP_SIMULATION 0x0002 +#define HUP_VR_CONTROLS 0x0003 +#define HUP_SPORTS_CONTROLS 0x0004 +#define HUP_GAMING_CONTROLS 0x0005 +#define HUP_KEYBOARD 0x0007 +#define HUP_LED 0x0008 +#define HUP_BUTTON 0x0009 +#define HUP_ORDINALS 0x000a +#define HUP_TELEPHONY 0x000b +#define HUP_CONSUMER 0x000c +#define HUP_DIGITIZERS 0x000d +#define HUP_PHYSICAL_IFACE 0x000e +#define HUP_UNICODE 0x0010 +#define HUP_ALPHANUM_DISPLAY 0x0014 +#define HUP_MONITOR 0x0080 +#define HUP_MONITOR_ENUM_VAL 0x0081 +#define HUP_VESA_VC 0x0082 +#define HUP_VESA_CMD 0x0083 +#define HUP_POWER 0x0084 +#define HUP_BATTERY 0x0085 +#define HUP_BARCODE_SCANNER 0x008b +#define HUP_SCALE 0x008c +#define HUP_CAMERA_CONTROL 0x0090 +#define HUP_ARCADE 0x0091 +#define HUP_APPLE 0x00ff +#define HUP_MICROSOFT 0xff00 + +/* Usages, Power Device */ +#define HUP_INAME 0x0001 +#define HUP_PRESENT_STATUS 0x0002 +#define HUP_CHANGED_STATUS 0x0003 +#define HUP_UPS 0x0004 +#define HUP_POWER_SUPPLY 0x0005 +#define HUP_BATTERY_SYSTEM 0x0010 +#define HUP_BATTERY_SYSTEM_ID 0x0011 +#define HUP_PD_BATTERY 0x0012 +#define HUP_BATTERY_ID 0x0013 +#define HUP_CHARGER 0x0014 +#define HUP_CHARGER_ID 0x0015 +#define HUP_POWER_CONVERTER 0x0016 +#define HUP_POWER_CONVERTER_ID 0x0017 +#define HUP_OUTLET_SYSTEM 0x0018 +#define HUP_OUTLET_SYSTEM_ID 0x0019 +#define HUP_INPUT 0x001a +#define HUP_INPUT_ID 0x001b +#define HUP_OUTPUT 0x001c +#define HUP_OUTPUT_ID 0x001d +#define HUP_FLOW 0x001e +#define HUP_FLOW_ID 0x001f +#define HUP_OUTLET 0x0020 +#define HUP_OUTLET_ID 0x0021 +#define HUP_GANG 0x0022 +#define HUP_GANG_ID 0x0023 +#define HUP_POWER_SUMMARY 0x0024 +#define HUP_POWER_SUMMARY_ID 0x0025 +#define HUP_VOLTAGE 0x0030 +#define HUP_CURRENT 0x0031 +#define HUP_FREQUENCY 0x0032 +#define HUP_APPARENT_POWER 0x0033 +#define HUP_ACTIVE_POWER 0x0034 +#define HUP_PERCENT_LOAD 0x0035 +#define HUP_TEMPERATURE 0x0036 +#define HUP_HUMIDITY 0x0037 +#define HUP_BADCOUNT 0x0038 +#define HUP_CONFIG_VOLTAGE 0x0040 +#define HUP_CONFIG_CURRENT 0x0041 +#define HUP_CONFIG_FREQUENCY 0x0042 +#define HUP_CONFIG_APP_POWER 0x0043 +#define HUP_CONFIG_ACT_POWER 0x0044 +#define HUP_CONFIG_PERCENT_LOAD 0x0045 +#define HUP_CONFIG_TEMPERATURE 0x0046 +#define HUP_CONFIG_HUMIDITY 0x0047 +#define HUP_SWITCHON_CONTROL 0x0050 +#define HUP_SWITCHOFF_CONTROL 0x0051 +#define HUP_TOGGLE_CONTROL 0x0052 +#define HUP_LOW_VOLT_TRANSF 0x0053 +#define HUP_HIGH_VOLT_TRANSF 0x0054 +#define HUP_DELAYBEFORE_REBOOT 0x0055 +#define HUP_DELAYBEFORE_STARTUP 0x0056 +#define HUP_DELAYBEFORE_SHUTDWN 0x0057 +#define HUP_TEST 0x0058 +#define HUP_MODULE_RESET 0x0059 +#define HUP_AUDIBLE_ALRM_CTL 0x005a +#define HUP_PRESENT 0x0060 +#define HUP_GOOD 0x0061 +#define HUP_INTERNAL_FAILURE 0x0062 +#define HUP_PD_VOLT_OUTOF_RANGE 0x0063 +#define HUP_FREQ_OUTOFRANGE 0x0064 +#define HUP_OVERLOAD 0x0065 +#define HUP_OVERCHARGED 0x0066 +#define HUP_OVERTEMPERATURE 0x0067 +#define HUP_SHUTDOWN_REQUESTED 0x0068 +#define HUP_SHUTDOWN_IMMINENT 0x0069 +#define HUP_SWITCH_ON_OFF 0x006b +#define HUP_SWITCHABLE 0x006c +#define HUP_USED 0x006d +#define HUP_BOOST 0x006e +#define HUP_BUCK 0x006f +#define HUP_INITIALIZED 0x0070 +#define HUP_TESTED 0x0071 +#define HUP_AWAITING_POWER 0x0072 +#define HUP_COMMUNICATION_LOST 0x0073 +#define HUP_IMANUFACTURER 0x00fd +#define HUP_IPRODUCT 0x00fe +#define HUP_ISERIALNUMBER 0x00ff + +/* Usages, Battery */ +#define HUB_SMB_BATTERY_MODE 0x0001 +#define HUB_SMB_BATTERY_STATUS 0x0002 +#define HUB_SMB_ALARM_WARNING 0x0003 +#define HUB_SMB_CHARGER_MODE 0x0004 +#define HUB_SMB_CHARGER_STATUS 0x0005 +#define HUB_SMB_CHARGER_SPECINF 0x0006 +#define HUB_SMB_SELECTR_STATE 0x0007 +#define HUB_SMB_SELECTR_PRESETS 0x0008 +#define HUB_SMB_SELECTR_INFO 0x0009 +#define HUB_SMB_OPT_MFGFUNC1 0x0010 +#define HUB_SMB_OPT_MFGFUNC2 0x0011 +#define HUB_SMB_OPT_MFGFUNC3 0x0012 +#define HUB_SMB_OPT_MFGFUNC4 0x0013 +#define HUB_SMB_OPT_MFGFUNC5 0x0014 +#define HUB_CONNECTIONTOSMBUS 0x0015 +#define HUB_OUTPUT_CONNECTION 0x0016 +#define HUB_CHARGER_CONNECTION 0x0017 +#define HUB_BATTERY_INSERTION 0x0018 +#define HUB_USENEXT 0x0019 +#define HUB_OKTOUSE 0x001a +#define HUB_BATTERY_SUPPORTED 0x001b +#define HUB_SELECTOR_REVISION 0x001c +#define HUB_CHARGING_INDICATOR 0x001d +#define HUB_MANUFACTURER_ACCESS 0x0028 +#define HUB_REM_CAPACITY_LIM 0x0029 +#define HUB_REM_TIME_LIM 0x002a +#define HUB_ATRATE 0x002b +#define HUB_CAPACITY_MODE 0x002c +#define HUB_BCAST_TO_CHARGER 0x002d +#define HUB_PRIMARY_BATTERY 0x002e +#define HUB_CHANGE_CONTROLLER 0x002f +#define HUB_TERMINATE_CHARGE 0x0040 +#define HUB_TERMINATE_DISCHARGE 0x0041 +#define HUB_BELOW_REM_CAP_LIM 0x0042 +#define HUB_REM_TIME_LIM_EXP 0x0043 +#define HUB_CHARGING 0x0044 +#define HUB_DISCHARGING 0x0045 +#define HUB_FULLY_CHARGED 0x0046 +#define HUB_FULLY_DISCHARGED 0x0047 +#define HUB_CONDITIONING_FLAG 0x0048 +#define HUB_ATRATE_OK 0x0049 +#define HUB_SMB_ERROR_CODE 0x004a +#define HUB_NEED_REPLACEMENT 0x004b +#define HUB_ATRATE_TIMETOFULL 0x0060 +#define HUB_ATRATE_TIMETOEMPTY 0x0061 +#define HUB_AVERAGE_CURRENT 0x0062 +#define HUB_MAXERROR 0x0063 +#define HUB_REL_STATEOF_CHARGE 0x0064 +#define HUB_ABS_STATEOF_CHARGE 0x0065 +#define HUB_REM_CAPACITY 0x0066 +#define HUB_FULLCHARGE_CAPACITY 0x0067 +#define HUB_RUNTIMETO_EMPTY 0x0068 +#define HUB_AVERAGETIMETO_EMPTY 0x0069 +#define HUB_AVERAGETIMETO_FULL 0x006a +#define HUB_CYCLECOUNT 0x006b +#define HUB_BATTPACKMODEL_LEVEL 0x0080 +#define HUB_INTERNAL_CHARGE_CTL 0x0081 +#define HUB_PRIMARY_BATTERY_SUP 0x0082 +#define HUB_DESIGN_CAPACITY 0x0083 +#define HUB_SPECIFICATION_INFO 0x0084 +#define HUB_MANUFACTURER_DATE 0x0085 +#define HUB_SERIAL_NUMBER 0x0086 +#define HUB_IMANUFACTURERNAME 0x0087 +#define HUB_IDEVICENAME 0x0088 +#define HUB_IDEVICECHEMISTERY 0x0089 +#define HUB_MANUFACTURERDATA 0x008a +#define HUB_RECHARGABLE 0x008b +#define HUB_WARN_CAPACITY_LIM 0x008c +#define HUB_CAPACITY_GRANUL1 0x008d +#define HUB_CAPACITY_GRANUL2 0x008e +#define HUB_IOEM_INFORMATION 0x008f +#define HUB_INHIBIT_CHARGE 0x00c0 +#define HUB_ENABLE_POLLING 0x00c1 +#define HUB_RESTORE_TO_ZERO 0x00c2 +#define HUB_AC_PRESENT 0x00d0 +#define HUB_BATTERY_PRESENT 0x00d1 +#define HUB_POWER_FAIL 0x00d2 +#define HUB_ALARM_INHIBITED 0x00d3 +#define HUB_THERMISTOR_UNDRANGE 0x00d4 +#define HUB_THERMISTOR_HOT 0x00d5 +#define HUB_THERMISTOR_COLD 0x00d6 +#define HUB_THERMISTOR_OVERANGE 0x00d7 +#define HUB_BS_VOLT_OUTOF_RANGE 0x00d8 +#define HUB_BS_CURR_OUTOF_RANGE 0x00d9 +#define HUB_BS_CURR_NOT_REGULTD 0x00da +#define HUB_BS_VOLT_NOT_REGULTD 0x00db +#define HUB_MASTER_MODE 0x00dc +#define HUB_CHARGER_SELECTR_SUP 0x00f0 +#define HUB_CHARGER_SPEC 0x00f1 +#define HUB_LEVEL2 0x00f2 +#define HUB_LEVEL3 0x00f3 + +/* Usages, generic desktop */ +#define HUG_POINTER 0x0001 +#define HUG_MOUSE 0x0002 +#define HUG_FN_KEY 0x0003 +#define HUG_JOYSTICK 0x0004 +#define HUG_GAME_PAD 0x0005 +#define HUG_KEYBOARD 0x0006 +#define HUG_KEYPAD 0x0007 +#define HUG_X 0x0030 +#define HUG_Y 0x0031 +#define HUG_Z 0x0032 +#define HUG_RX 0x0033 +#define HUG_RY 0x0034 +#define HUG_RZ 0x0035 +#define HUG_SLIDER 0x0036 +#define HUG_DIAL 0x0037 +#define HUG_WHEEL 0x0038 +#define HUG_HAT_SWITCH 0x0039 +#define HUG_COUNTED_BUFFER 0x003a +#define HUG_BYTE_COUNT 0x003b +#define HUG_MOTION_WAKEUP 0x003c +#define HUG_VX 0x0040 +#define HUG_VY 0x0041 +#define HUG_VZ 0x0042 +#define HUG_VBRX 0x0043 +#define HUG_VBRY 0x0044 +#define HUG_VBRZ 0x0045 +#define HUG_VNO 0x0046 +#define HUG_TWHEEL 0x0048 +#define HUG_SYSTEM_CONTROL 0x0080 +#define HUG_SYSTEM_POWER_DOWN 0x0081 +#define HUG_SYSTEM_SLEEP 0x0082 +#define HUG_SYSTEM_WAKEUP 0x0083 +#define HUG_SYSTEM_CONTEXT_MENU 0x0084 +#define HUG_SYSTEM_MAIN_MENU 0x0085 +#define HUG_SYSTEM_APP_MENU 0x0086 +#define HUG_SYSTEM_MENU_HELP 0x0087 +#define HUG_SYSTEM_MENU_EXIT 0x0088 +#define HUG_SYSTEM_MENU_SELECT 0x0089 +#define HUG_SYSTEM_MENU_RIGHT 0x008a +#define HUG_SYSTEM_MENU_LEFT 0x008b +#define HUG_SYSTEM_MENU_UP 0x008c +#define HUG_SYSTEM_MENU_DOWN 0x008d + +/* Usages, Digitizers */ +#define HUD_UNDEFINED 0x0000 +#define HUD_DIGITIZER 0x0001 +#define HUD_PEN 0x0002 +#define HUD_TOUCHSCREEN 0x0004 +#define HUD_TOUCHPAD 0x0005 +#define HUD_FINGER 0x0022 +#define HUD_TIP_PRESSURE 0x0030 +#define HUD_BARREL_PRESSURE 0x0031 +#define HUD_IN_RANGE 0x0032 +#define HUD_TOUCH 0x0033 +#define HUD_UNTOUCH 0x0034 +#define HUD_TAP 0x0035 +#define HUD_QUALITY 0x0036 +#define HUD_DATA_VALID 0x0037 +#define HUD_TRANSDUCER_INDEX 0x0038 +#define HUD_TABLET_FKEYS 0x0039 +#define HUD_PROGRAM_CHANGE_KEYS 0x003a +#define HUD_BATTERY_STRENGTH 0x003b +#define HUD_INVERT 0x003c +#define HUD_X_TILT 0x003d +#define HUD_Y_TILT 0x003e +#define HUD_AZIMUTH 0x003f +#define HUD_ALTITUDE 0x0040 +#define HUD_TWIST 0x0041 +#define HUD_TIP_SWITCH 0x0042 +#define HUD_SEC_TIP_SWITCH 0x0043 +#define HUD_BARREL_SWITCH 0x0044 +#define HUD_ERASER 0x0045 +#define HUD_TABLET_PICK 0x0046 + +/* Usages, LED */ +#define HUL_NUM_LOCK 0x0001 +#define HUL_CAPS_LOCK 0x0002 +#define HUL_SCROLL_LOCK 0x0003 +#define HUL_COMPOSE 0x0004 +#define HUL_KANA 0x0005 + +/* Usages, Consumer */ +#define HUC_AC_PAN 0x0238 + +#define HID_USAGE2(p, u) (((p) << 16) | u) +#define HID_GET_USAGE(u) ((u) & 0xffff) +#define HID_GET_USAGE_PAGE(u) (((u) >> 16) & 0xffff) + +#define HCOLL_PHYSICAL 0 +#define HCOLL_APPLICATION 1 +#define HCOLL_LOGICAL 2 + +/* Bits in the input/output/feature items */ +#define HIO_CONST 0x001 +#define HIO_VARIABLE 0x002 +#define HIO_RELATIVE 0x004 +#define HIO_WRAP 0x008 +#define HIO_NONLINEAR 0x010 +#define HIO_NOPREF 0x020 +#define HIO_NULLSTATE 0x040 +#define HIO_VOLATILE 0x080 +#define HIO_BUFBYTES 0x100 + +/* Valid values for the country codes */ +#define HCC_UNDEFINED 0x00 +#define HCC_MAX 0x23 + +#endif /* _HIDHID_H_ */ diff --git a/sys/dev/hid/hidkbd.c b/sys/dev/hid/hidkbd.c new file mode 100644 index 00000000000..241ffc7bc3e --- /dev/null +++ b/sys/dev/hid/hidkbd.c @@ -0,0 +1,642 @@ +/* $OpenBSD: hidkbd.c,v 1.1 2016/01/08 15:54:13 jcs Exp $ */ +/* $NetBSD: ukbd.c,v 1.85 2003/03/11 16:44:00 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HIDKBD_DEBUG +#define DPRINTF(x) do { if (hidkbddebug) printf x; } while (0) +#define DPRINTFN(n,x) do { if (hidkbddebug>(n)) printf x; } while (0) +int hidkbddebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define PRESS 0x000 +#define RELEASE 0x100 +#define CODEMASK 0x0ff + +#if defined(WSDISPLAY_COMPAT_RAWKBD) +#define NN 0 /* no translation */ +/* + * Translate USB keycodes to US keyboard XT scancodes. + * Scancodes >= 0x80 represent EXTENDED keycodes. + * + * See http://www.microsoft.com/whdc/archive/Scancode.mspx + */ +const u_int8_t hidkbd_trtab[256] = { + NN, NN, NN, NN, 0x1e, 0x30, 0x2e, 0x20, /* 00 - 07 */ + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, /* 08 - 0f */ + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, /* 10 - 17 */ + 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, /* 18 - 1f */ + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, /* 20 - 27 */ + 0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, /* 28 - 2f */ + 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34, /* 30 - 37 */ + 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, /* 38 - 3f */ + 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xaa, 0x46, /* 40 - 47 */ + 0x7f, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd, /* 48 - 4f */ + 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, /* 50 - 57 */ + 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, /* 58 - 5f */ + 0x48, 0x49, 0x52, 0x53, 0x56, 0xdd, 0xde, 0x59, /* 60 - 67 */ + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, /* 68 - 6f */ + 0x6c, 0x6d, 0x6e, 0x76, 0x97, NN, 0x93, 0x95, /* 70 - 77 */ + 0x91, 0x92, 0x94, 0x9a, 0x96, 0x98, 0x99, 0xa0, /* 78 - 7f */ + 0xb0, 0xae, NN, NN, NN, 0x7e, NN, 0x73, /* 80 - 87 */ + 0x70, 0x7d, 0x79, 0x7b, 0x5c, NN, NN, NN, /* 88 - 8f */ + NN, NN, 0x78, 0x77, 0x76, NN, NN, NN, /* 90 - 97 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9f */ + NN, NN, NN, NN, NN, NN, NN, NN, /* a0 - a7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* a8 - af */ + NN, NN, NN, NN, NN, NN, NN, NN, /* b0 - b7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* b8 - bf */ + NN, NN, NN, NN, NN, NN, NN, NN, /* c0 - c7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* c8 - cf */ + NN, NN, NN, NN, NN, NN, NN, NN, /* d0 - d7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* d8 - df */ + 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, /* e0 - e7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* e8 - ef */ + NN, NN, NN, NN, NN, NN, NN, NN, /* f0 - f7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* f8 - ff */ +}; +#endif /* defined(WSDISPLAY_COMPAT_RAWKBD) */ + +#define KEY_ERROR 0x01 + +#ifdef HIDKBD_DEBUG +#define HIDKBDTRACESIZE 64 +struct hidkbdtraceinfo { + int unit; + struct timeval tv; + struct hidkbd_data ud; +}; +struct hidkbdtraceinfo hidkbdtracedata[HIDKBDTRACESIZE]; +int hidkbdtraceindex = 0; +int hidkbdtrace = 0; +void hidkbdtracedump(void); +void +hidkbdtracedump(void) +{ + int i; + for (i = 0; i < HIDKBDTRACESIZE; i++) { + struct hidkbdtraceinfo *p = + &hidkbdtracedata[(i+hidkbdtraceindex)%HIDKBDTRACESIZE]; + printf("%lld.%06ld: mod=0x%02x key0=0x%02x key1=0x%02x " + "key2=0x%02x key3=0x%02x\n", + (long long)p->tv.tv_sec, p->tv.tv_usec, + p->ud.modifiers, p->ud.keycode[0], p->ud.keycode[1], + p->ud.keycode[2], p->ud.keycode[3]); + } +} +#endif + +int hidkbd_is_console; + +const char *hidkbd_parse_desc(struct hidkbd *, int, void *, int); + +void (*hidkbd_bell_fn)(void *, u_int, u_int, u_int, int); +void *hidkbd_bell_fn_arg; + +void hidkbd_decode(struct hidkbd *, struct hidkbd_data *); +void hidkbd_delayed_decode(void *addr); + +extern const struct wscons_keydesc ukbd_keydesctab[]; + +struct wskbd_mapdata ukbd_keymapdata = { + ukbd_keydesctab +}; + +int +hidkbd_attach(struct device *self, struct hidkbd *kbd, int console, + uint32_t qflags, int id, void *desc, int dlen) +{ + const char *parserr; + + kbd->sc_var = NULL; + + parserr = hidkbd_parse_desc(kbd, id, desc, dlen); + if (parserr != NULL) { + printf(": %s\n", parserr); + return ENXIO; + } + +#ifdef DIAGNOSTIC + printf(": %d variable keys, %d key codes", + kbd->sc_nvar, kbd->sc_nkeycode); +#endif + + kbd->sc_device = self; + kbd->sc_debounce = (qflags & HIDKBD_SPUR_BUT_UP) != 0; + + /* + * Remember if we're the console keyboard. + * + * XXX This always picks the first (USB) keyboard to attach, + * but what else can we really do? + */ + if (console) { + kbd->sc_console_keyboard = hidkbd_is_console; + /* Don't let any other keyboard have it. */ + hidkbd_is_console = 0; + } + + timeout_set(&kbd->sc_delay, hidkbd_delayed_decode, kbd); + + return 0; +} + +void +hidkbd_attach_wskbd(struct hidkbd *kbd, kbd_t layout, + const struct wskbd_accessops *accessops) +{ + struct wskbddev_attach_args a; + + ukbd_keymapdata.layout = layout; + + a.console = kbd->sc_console_keyboard; + a.keymap = &ukbd_keymapdata; + a.accessops = accessops; + a.accesscookie = kbd->sc_device; + kbd->sc_wskbddev = config_found(kbd->sc_device, &a, wskbddevprint); +} + +int +hidkbd_detach(struct hidkbd *kbd, int flags) +{ + int rv = 0; + + DPRINTF(("hidkbd_detach: sc=%p flags=%d\n", kbd->sc_device, flags)); + + if (kbd->sc_console_keyboard) { +#if 0 + /* + * XXX Should probably disconnect our consops, + * XXX and either notify some other keyboard that + * XXX it can now be the console, or if there aren't + * XXX any more USB keyboards, set hidkbd_is_console + * XXX back to 1 so that the next USB keyboard attached + * XXX to the system will get it. + */ + panic("hidkbd_detach: console keyboard"); +#else + /* + * Disconnect our consops and set hidkbd_is_console + * back to 1 so that the next USB keyboard attached + * to the system will get it. + * XXX Should notify some other keyboard that it can be + * XXX console, if there are any other keyboards. + */ + printf("%s: was console keyboard\n", + kbd->sc_device->dv_xname); + wskbd_cndetach(); + hidkbd_is_console = 1; +#endif + } + /* No need to do reference counting of hidkbd, wskbd has all the goo */ + if (kbd->sc_wskbddev != NULL) + rv = config_detach(kbd->sc_wskbddev, flags); + + if (kbd->sc_var != NULL) + free(kbd->sc_var, M_DEVBUF, 0); + + return (rv); +} + +void +hidkbd_input(struct hidkbd *kbd, uint8_t *data, u_int len) +{ + struct hidkbd_data *ud = &kbd->sc_ndata; + int i; + +#ifdef HIDKBD_DEBUG + if (hidkbddebug > 5) { + printf("hidkbd_input: data"); + for (i = 0; i < len; i++) + printf(" 0x%02x", data[i]); + printf("\n"); + } +#endif + + /* extract variable keys */ + for (i = 0; i < kbd->sc_nvar; i++) + ud->var[i] = (u_int8_t)hid_get_data(data, len, + &kbd->sc_var[i].loc); + + /* extract keycodes */ + memcpy(ud->keycode, data + kbd->sc_keycodeloc.pos / 8, + kbd->sc_nkeycode); + + if (kbd->sc_debounce && !kbd->sc_polling) { + /* + * Some keyboards have a peculiar quirk. They sometimes + * generate a key up followed by a key down for the same + * key after about 10 ms. + * We avoid this bug by holding off decoding for 20 ms. + */ + kbd->sc_data = *ud; + timeout_add_msec(&kbd->sc_delay, 20); +#ifdef DDB + } else if (kbd->sc_console_keyboard && !kbd->sc_polling) { + /* + * For the console keyboard we can't deliver CTL-ALT-ESC + * from the interrupt routine. Doing so would start + * polling from inside the interrupt routine and that + * loses bigtime. + */ + /* if (!timeout_pending(&kbd->sc_delay)) */ { + kbd->sc_data = *ud; + timeout_add(&kbd->sc_delay, 1); + } +#endif + } else { + hidkbd_decode(kbd, ud); + } +} + +void +hidkbd_delayed_decode(void *addr) +{ + struct hidkbd *kbd = addr; + + hidkbd_decode(kbd, &kbd->sc_data); +} + +void +hidkbd_decode(struct hidkbd *kbd, struct hidkbd_data *ud) +{ + u_int16_t ibuf[MAXKEYS]; /* chars events */ + int s; + int nkeys, i, j; + int key; +#define ADDKEY(c) ibuf[nkeys++] = (c) + +#ifdef HIDKBD_DEBUG + /* + * Keep a trace of the last events. Using printf changes the + * timing, so this can be useful sometimes. + */ + if (hidkbdtrace) { + struct hidkbdtraceinfo *p = &hidkbdtracedata[hidkbdtraceindex]; + p->unit = kbd->sc_device->dv_unit; + microtime(&p->tv); + p->ud = *ud; + if (++hidkbdtraceindex >= HIDKBDTRACESIZE) + hidkbdtraceindex = 0; + } + if (hidkbddebug > 5) { + struct timeval tv; + microtime(&tv); + DPRINTF((" at %lld.%06ld mod=0x%02x key0=0x%02x key1=0x%02x " + "key2=0x%02x key3=0x%02x\n", + (long long)tv.tv_sec, tv.tv_usec, + ud->modifiers, ud->keycode[0], ud->keycode[1], + ud->keycode[2], ud->keycode[3])); + } +#endif + + if (ud->keycode[0] == KEY_ERROR) { + DPRINTF(("hidkbd_input: KEY_ERROR\n")); + return; /* ignore */ + } + nkeys = 0; + + for (i = 0; i < kbd->sc_nvar; i++) + if ((kbd->sc_odata.var[i] & kbd->sc_var[i].mask) != + (ud->var[i] & kbd->sc_var[i].mask)) { + ADDKEY(kbd->sc_var[i].key | + ((ud->var[i] & kbd->sc_var[i].mask) ? + PRESS : RELEASE)); + } + + if (memcmp(ud->keycode, kbd->sc_odata.keycode, kbd->sc_nkeycode) != 0) { + /* Check for released keys. */ + for (i = 0; i < kbd->sc_nkeycode; i++) { + key = kbd->sc_odata.keycode[i]; + if (key == 0) + continue; + for (j = 0; j < kbd->sc_nkeycode; j++) + if (key == ud->keycode[j]) + goto rfound; + DPRINTFN(3,("hidkbd_decode: relse key=0x%02x\n", key)); + ADDKEY(key | RELEASE); + rfound: + ; + } + + /* Check for pressed keys. */ + for (i = 0; i < kbd->sc_nkeycode; i++) { + key = ud->keycode[i]; + if (key == 0) + continue; + for (j = 0; j < kbd->sc_nkeycode; j++) + if (key == kbd->sc_odata.keycode[j]) + goto pfound; + DPRINTFN(2,("hidkbd_decode: press key=0x%02x\n", key)); + ADDKEY(key | PRESS); + pfound: + ; + } + } + kbd->sc_odata = *ud; + + if (nkeys == 0) + return; + + if (kbd->sc_polling) { + DPRINTFN(1,("hidkbd_decode: pollchar = 0x%03x\n", ibuf[0])); + memcpy(kbd->sc_pollchars, ibuf, nkeys * sizeof(u_int16_t)); + kbd->sc_npollchar = nkeys; + return; + } + + if (kbd->sc_wskbddev == NULL) + return; + +#ifdef WSDISPLAY_COMPAT_RAWKBD + if (kbd->sc_rawkbd) { + u_char cbuf[MAXKEYS * 2]; + int c; + + for (i = j = 0; i < nkeys; i++) { + key = ibuf[i]; + c = hidkbd_trtab[key & CODEMASK]; + if (c == NN) + continue; + if (c & 0x80) + cbuf[j++] = 0xe0; + cbuf[j] = c & 0x7f; + if (key & RELEASE) + cbuf[j] |= 0x80; + DPRINTFN(1,("hidkbd_decode: raw = %s0x%02x\n", + c & 0x80 ? "0xe0 " : "", + cbuf[j])); + j++; + } + s = spltty(); + wskbd_rawinput(kbd->sc_wskbddev, cbuf, j); + + /* + * Pass audio keys to wskbd_input anyway. + */ + for (i = 0; i < nkeys; i++) { + key = ibuf[i]; + switch (key & CODEMASK) { + case 127: + case 128: + case 129: + wskbd_input(kbd->sc_wskbddev, + key & RELEASE ? WSCONS_EVENT_KEY_UP : + WSCONS_EVENT_KEY_DOWN, key & CODEMASK); + break; + } + } + splx(s); + + return; + } +#endif + + s = spltty(); + for (i = 0; i < nkeys; i++) { + key = ibuf[i]; + wskbd_input(kbd->sc_wskbddev, + key&RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN, + key&CODEMASK); + } + splx(s); +#undef ADDKEY +} + +int +hidkbd_enable(struct hidkbd *kbd, int on) +{ + if (kbd->sc_enabled == on) + return EBUSY; + + kbd->sc_enabled = on; + return 0; +} + +int +hidkbd_set_leds(struct hidkbd *kbd, int leds, uint8_t *report) +{ + if (kbd->sc_leds == leds) + return 0; + + kbd->sc_leds = leds; + + /* + * This is not totally correct, since we did not check the + * report size from the descriptor but for keyboards it should + * just be a single byte with the relevant bits set. + */ + *report = 0; + if ((leds & WSKBD_LED_SCROLL) && kbd->sc_scroloc.size == 1) + *report |= 1 << kbd->sc_scroloc.pos; + if ((leds & WSKBD_LED_NUM) && kbd->sc_numloc.size == 1) + *report |= 1 << kbd->sc_numloc.pos; + if ((leds & WSKBD_LED_CAPS) && kbd->sc_capsloc.size == 1) + *report |= 1 << kbd->sc_capsloc.pos; + if ((leds & WSKBD_LED_COMPOSE) && kbd->sc_compose.size == 1) + *report |= 1 << kbd->sc_compose.pos; + + return 1; +} + +int +hidkbd_ioctl(struct hidkbd *kbd, u_long cmd, caddr_t data, int flag, + struct proc *p) +{ + switch (cmd) { + case WSKBDIO_GETLEDS: + *(int *)data = kbd->sc_leds; + return (0); + case WSKBDIO_COMPLEXBELL: +#define d ((struct wskbd_bell_data *)data) + hidkbd_bell(d->pitch, d->period, d->volume, 0); +#undef d + return (0); +#ifdef WSDISPLAY_COMPAT_RAWKBD + case WSKBDIO_SETMODE: + DPRINTF(("hidkbd_ioctl: set raw = %d\n", *(int *)data)); + kbd->sc_rawkbd = *(int *)data == WSKBD_RAW; + return (0); +#endif + } + return (-1); +} + +void +hidkbd_cngetc(struct hidkbd *kbd, u_int *type, int *data) +{ + int c; + + c = kbd->sc_pollchars[0]; + kbd->sc_npollchar--; + memcpy(kbd->sc_pollchars, kbd->sc_pollchars+1, + kbd->sc_npollchar * sizeof(u_int16_t)); + *type = c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; + *data = c & CODEMASK; +} + +void +hidkbd_bell(u_int pitch, u_int period, u_int volume, int poll) +{ + if (hidkbd_bell_fn != NULL) + (*hidkbd_bell_fn)(hidkbd_bell_fn_arg, pitch, period, + volume, poll); +} + +void +hidkbd_hookup_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg) +{ + if (hidkbd_bell_fn == NULL) { + hidkbd_bell_fn = fn; + hidkbd_bell_fn_arg = arg; + } +} + +const char * +hidkbd_parse_desc(struct hidkbd *kbd, int id, void *desc, int dlen) +{ + struct hid_data *d; + struct hid_item h; + unsigned int i, ivar = 0; + + kbd->sc_nkeycode = 0; + + d = hid_start_parse(desc, dlen, hid_input); + while (hid_get_item(d, &h)) { + if (h.kind != hid_input || (h.flags & HIO_CONST) || + HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD || + h.report_ID != id) + continue; + if (h.flags & HIO_VARIABLE) + ivar++; + } + hid_end_parse(d); + + if (ivar > MAXVARS) { + DPRINTF((": too many variable keys\n")); + ivar = MAXVARS; + } + + kbd->sc_nvar = ivar; + kbd->sc_var = (struct hidkbd_variable *)mallocarray(ivar, + sizeof(struct hidkbd_variable), M_DEVBUF, M_NOWAIT); + + if (!kbd->sc_var) + return NULL; + + i = 0; + + d = hid_start_parse(desc, dlen, hid_input); + while (hid_get_item(d, &h)) { + if (h.kind != hid_input || (h.flags & HIO_CONST) || + HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD || + h.report_ID != id) + continue; + + DPRINTF(("hidkbd: usage=0x%x flags=0x%x pos=%d size=%d cnt=%d", + h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count)); + if (h.flags & HIO_VARIABLE) { + /* variable reports should be one bit each */ + if (h.loc.size != 1) { + DPRINTF((": bad variable size\n")); + continue; + } + + /* variable report */ + if (i < MAXVARS) { + kbd->sc_var[i].loc = h.loc; + kbd->sc_var[i].mask = 1 << (i % 8); + kbd->sc_var[i].key = HID_GET_USAGE(h.usage); + i++; + } + } else { + /* keys array should be in bytes, on a byte boundary */ + if (h.loc.size != 8) { + DPRINTF((": key code size != 8\n")); + continue; + } + if (h.loc.pos % 8 != 0) { + DPRINTF((": array not on byte boundary")); + continue; + } + if (kbd->sc_nkeycode != 0) { + DPRINTF((": ignoring multiple arrays\n")); + continue; + } + kbd->sc_keycodeloc = h.loc; + if (h.loc.count > MAXKEYCODE) { + DPRINTF((": ignoring extra key codes")); + kbd->sc_nkeycode = MAXKEYCODE; + } else + kbd->sc_nkeycode = h.loc.count; + } + DPRINTF(("\n")); + } + hid_end_parse(d); + + /* don't attach if no keys... */ + if (kbd->sc_nkeycode == 0 && ivar == 0) + return "no usable key codes array"; + + hid_locate(desc, dlen, HID_USAGE2(HUP_LED, HUL_NUM_LOCK), + id, hid_output, &kbd->sc_numloc, NULL); + hid_locate(desc, dlen, HID_USAGE2(HUP_LED, HUL_CAPS_LOCK), + id, hid_output, &kbd->sc_capsloc, NULL); + hid_locate(desc, dlen, HID_USAGE2(HUP_LED, HUL_SCROLL_LOCK), + id, hid_output, &kbd->sc_scroloc, NULL); + hid_locate(desc, dlen, HID_USAGE2(HUP_LED, HUL_COMPOSE), + id, hid_output, &kbd->sc_compose, NULL); + + return (NULL); +} diff --git a/sys/dev/hid/hidkbdsc.h b/sys/dev/hid/hidkbdsc.h new file mode 100644 index 00000000000..02c67fb2ff0 --- /dev/null +++ b/sys/dev/hid/hidkbdsc.h @@ -0,0 +1,105 @@ +/* $OpenBSD: hidkbdsc.h,v 1.1 2016/01/08 15:54:13 jcs Exp $ */ +/* $NetBSD: ukbd.c,v 1.85 2003/03/11 16:44:00 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define MAXKEYCODE 6 +#define MAXVARS 128 + +#define MAXKEYS (MAXVARS+2*MAXKEYCODE) + +/* quirks */ +#define HIDKBD_SPUR_BUT_UP 0x001 /* spurious button up events */ + +struct hidkbd_variable { + struct hid_location loc; + u_int8_t mask; + u_int8_t key; +}; + +struct hidkbd_data { + u_int8_t keycode[MAXKEYCODE]; + u_int8_t var[MAXVARS]; +}; + +struct hidkbd { + /* stored data */ + struct hidkbd_data sc_ndata; + struct hidkbd_data sc_odata; + + /* input reports */ + u_int sc_nvar; + struct hidkbd_variable *sc_var; + + struct hid_location sc_keycodeloc; + u_int sc_nkeycode; + + /* output reports */ + struct hid_location sc_numloc; + struct hid_location sc_capsloc; + struct hid_location sc_scroloc; + struct hid_location sc_compose; + int sc_leds; + + /* state information */ + struct device *sc_device; + struct device *sc_wskbddev; + char sc_enabled; + + char sc_console_keyboard; /* we are the console keyboard */ + + char sc_debounce; /* for quirk handling */ + struct timeout sc_delay; /* for quirk handling */ + struct hidkbd_data sc_data; /* for quirk handling */ + + /* key repeat logic */ +#if defined(WSDISPLAY_COMPAT_RAWKBD) + int sc_rawkbd; +#endif /* defined(WSDISPLAY_COMPAT_RAWKBD) */ + + int sc_polling; + int sc_npollchar; + u_int16_t sc_pollchars[MAXKEYS]; +}; + +int hidkbd_attach(struct device *, struct hidkbd *, int, uint32_t, + int, void *, int); +void hidkbd_attach_wskbd(struct hidkbd *, kbd_t, + const struct wskbd_accessops *); +void hidkbd_bell(u_int, u_int, u_int, int); +void hidkbd_cngetc(struct hidkbd *, u_int *, int *); +int hidkbd_detach(struct hidkbd *, int); +int hidkbd_enable(struct hidkbd *, int); +void hidkbd_input(struct hidkbd *, uint8_t *, u_int); +int hidkbd_ioctl(struct hidkbd *, u_long, caddr_t, int, struct proc *); +int hidkbd_set_leds(struct hidkbd *, int, uint8_t *); + +extern int hidkbd_is_console; diff --git a/sys/dev/hid/hidkbdvar.h b/sys/dev/hid/hidkbdvar.h new file mode 100644 index 00000000000..17efebe3624 --- /dev/null +++ b/sys/dev/hid/hidkbdvar.h @@ -0,0 +1,34 @@ +/* $OpenBSD: hidkbdvar.h,v 1.1 2016/01/08 15:54:13 jcs Exp $ */ +/* $NetBSD: ukbd.c,v 1.85 2003/03/11 16:44:00 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void hidkbd_hookup_bell(void (*)(void *, u_int, u_int, u_int, int), void *); diff --git a/sys/dev/hid/hidms.c b/sys/dev/hid/hidms.c new file mode 100644 index 00000000000..e52c3cb7622 --- /dev/null +++ b/sys/dev/hid/hidms.c @@ -0,0 +1,478 @@ +/* $OpenBSD: hidms.c,v 1.1 2016/01/08 15:54:13 jcs Exp $ */ +/* $NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef HIDMS_DEBUG +#define DPRINTF(x) do { if (hidmsdebug) printf x; } while (0) +#define DPRINTFN(n,x) do { if (hidmsdebug>(n)) printf x; } while (0) +int hidmsdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define HIDMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) + +#define MOUSE_FLAGS_MASK (HIO_CONST | HIO_RELATIVE) +#define NOTMOUSE(f) (((f) & MOUSE_FLAGS_MASK) != HIO_RELATIVE) + +int +hidms_setup(struct device *self, struct hidms *ms, uint32_t quirks, + int id, void *desc, int dlen) +{ + struct hid_item h; + struct hid_data *d; + uint32_t flags; + int i, wheel, twheel; + + ms->sc_device = self; + ms->sc_rawmode = 1; + + ms->sc_flags = quirks; + + if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), id, + hid_input, &ms->sc_loc_x, &flags)) { + printf("\n%s: mouse has no X report\n", self->dv_xname); + return ENXIO; + } + switch(flags & MOUSE_FLAGS_MASK) { + case 0: + ms->sc_flags |= HIDMS_ABSX; + break; + case HIO_RELATIVE: + break; + default: + printf("\n%s: X report 0x%04x not supported\n", + self->dv_xname, flags); + return ENXIO; + } + + if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), id, + hid_input, &ms->sc_loc_y, &flags)) { + printf("\n%s: mouse has no Y report\n", self->dv_xname); + return ENXIO; + } + switch(flags & MOUSE_FLAGS_MASK) { + case 0: + ms->sc_flags |= HIDMS_ABSY; + break; + case HIO_RELATIVE: + break; + default: + printf("\n%s: Y report 0x%04x not supported\n", + self->dv_xname, flags); + return ENXIO; + } + + /* + * Try to guess the Z activator: check WHEEL, TWHEEL, and Z, + * in that order. + */ + + wheel = hid_locate(desc, dlen, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), id, + hid_input, &ms->sc_loc_z, &flags); + if (wheel == 0) + twheel = hid_locate(desc, dlen, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), id, + hid_input, &ms->sc_loc_z, &flags); + else + twheel = 0; + + if (wheel || twheel) { + if (NOTMOUSE(flags)) { + DPRINTF(("\n%s: Wheel report 0x%04x not supported\n", + self->dv_xname, flags)); + ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ + } else { + ms->sc_flags |= HIDMS_Z; + /* Wheels need the Z axis reversed. */ + ms->sc_flags ^= HIDMS_REVZ; + } + /* + * We might have both a wheel and Z direction; in this case, + * report the Z direction on the W axis. + * + * Otherwise, check for a W direction as an AC Pan input used + * on some newer mice. + */ + if (hid_locate(desc, dlen, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id, + hid_input, &ms->sc_loc_w, &flags)) { + if (NOTMOUSE(flags)) { + DPRINTF(("\n%s: Z report 0x%04x not supported\n", + self->dv_xname, flags)); + /* Bad Z coord, ignore it */ + ms->sc_loc_w.size = 0; + } + else + ms->sc_flags |= HIDMS_W; + } else if (hid_locate(desc, dlen, + HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), id, hid_input, + &ms->sc_loc_w, &flags)) { + ms->sc_flags |= HIDMS_W; + } + } else if (hid_locate(desc, dlen, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id, + hid_input, &ms->sc_loc_z, &flags)) { + if (NOTMOUSE(flags)) { + DPRINTF(("\n%s: Z report 0x%04x not supported\n", + self->dv_xname, flags)); + ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ + } else { + ms->sc_flags |= HIDMS_Z; + } + } + + /* + * The Microsoft Wireless Intellimouse 2.0 reports its wheel + * using 0x0048 (I've called it HUG_TWHEEL) and seems to expect + * us to know that the byte after the wheel is the tilt axis. + * There are no other HID axis descriptors other than X, Y and + * TWHEEL, so we report TWHEEL on the W axis. + */ + if (twheel) { + ms->sc_loc_w = ms->sc_loc_z; + ms->sc_loc_w.pos = ms->sc_loc_w.pos + 8; + ms->sc_flags |= HIDMS_W | HIDMS_LEADINGBYTE; + /* Wheels need their axis reversed. */ + ms->sc_flags ^= HIDMS_REVW; + } + + /* figure out the number of buttons */ + for (i = 1; i <= MAX_BUTTONS; i++) + if (!hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, i), id, + hid_input, &ms->sc_loc_btn[i - 1], NULL)) + break; + ms->sc_num_buttons = i - 1; + + if (hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, + HUD_TIP_SWITCH), id, hid_input, + &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){ + ms->sc_flags |= HIDMS_TIP; + ms->sc_num_buttons++; + } + + if (hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, + HUD_ERASER), id, hid_input, + &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){ + ms->sc_flags |= HIDMS_ERASER; + ms->sc_num_buttons++; + } + + if (hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, + HUD_BARREL_SWITCH), id, hid_input, + &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){ + ms->sc_flags |= HIDMS_BARREL; + ms->sc_num_buttons++; + } + + /* + * The Microsoft Wireless Notebook Optical Mouse seems to be in worse + * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and + * all of its other button positions are all off. It also reports that + * it has two addional buttons and a tilt wheel. + */ + if (ms->sc_flags & HIDMS_MS_BAD_CLASS) { + /* HIDMS_LEADINGBYTE cleared on purpose */ + ms->sc_flags = HIDMS_Z | HIDMS_SPUR_BUT_UP; + ms->sc_num_buttons = 3; + /* XXX change sc_hdev isize to 5? */ + /* 1st byte of descriptor report contains garbage */ + ms->sc_loc_x.pos = 16; + ms->sc_loc_y.pos = 24; + ms->sc_loc_z.pos = 32; + ms->sc_loc_btn[0].pos = 8; + ms->sc_loc_btn[1].pos = 9; + ms->sc_loc_btn[2].pos = 10; + } + /* Parse descriptors to get touch panel bounds */ + d = hid_start_parse(desc, dlen, hid_input); + while (hid_get_item(d, &h)) { + if (h.kind != hid_input || + HID_GET_USAGE_PAGE(h.usage) != HUP_GENERIC_DESKTOP) + continue; + DPRINTF(("hidms: usage=0x%x range %d..%d\n", + h.usage, h.logical_minimum, h.logical_maximum)); + switch (HID_GET_USAGE(h.usage)) { + case HUG_X: + if (ms->sc_flags & HIDMS_ABSX) { + ms->sc_tsscale.minx = h.logical_minimum; + ms->sc_tsscale.maxx = h.logical_maximum; + } + break; + case HUG_Y: + if (ms->sc_flags & HIDMS_ABSY) { + ms->sc_tsscale.miny = h.logical_minimum; + ms->sc_tsscale.maxy = h.logical_maximum; + } + break; + } + } + hid_end_parse(d); + return 0; +} + +void +hidms_attach(struct hidms *ms, const struct wsmouse_accessops *ops) +{ + struct wsmousedev_attach_args a; +#ifdef HIDMS_DEBUG + int i; +#endif + + printf(": %d button%s", + ms->sc_num_buttons, ms->sc_num_buttons <= 1 ? "" : "s"); + switch (ms->sc_flags & (HIDMS_Z | HIDMS_W)) { + case HIDMS_Z: + printf(", Z dir"); + break; + case HIDMS_W: + printf(", W dir"); + break; + case HIDMS_Z | HIDMS_W: + printf(", Z and W dir"); + break; + } + + if (ms->sc_flags & HIDMS_TIP) + printf(", tip"); + if (ms->sc_flags & HIDMS_BARREL) + printf(", barrel"); + if (ms->sc_flags & HIDMS_ERASER) + printf(", eraser"); + + printf("\n"); + +#ifdef HIDMS_DEBUG + DPRINTF(("hidms_attach: ms=%p\n", ms)); + DPRINTF(("hidms_attach: X\t%d/%d\n", + ms->sc_loc_x.pos, ms->sc_loc_x.size)); + DPRINTF(("hidms_attach: Y\t%d/%d\n", + ms->sc_loc_y.pos, ms->sc_loc_y.size)); + if (ms->sc_flags & HIDMS_Z) + DPRINTF(("hidms_attach: Z\t%d/%d\n", + ms->sc_loc_z.pos, ms->sc_loc_z.size)); + if (ms->sc_flags & HIDMS_W) + DPRINTF(("hidms_attach: W\t%d/%d\n", + ms->sc_loc_w.pos, ms->sc_loc_w.size)); + for (i = 1; i <= ms->sc_num_buttons; i++) { + DPRINTF(("hidms_attach: B%d\t%d/%d\n", + i, ms->sc_loc_btn[i - 1].pos, ms->sc_loc_btn[i - 1].size)); + } +#endif + + a.accessops = ops; + a.accesscookie = ms->sc_device; + ms->sc_wsmousedev = config_found(ms->sc_device, &a, wsmousedevprint); +} + +int +hidms_detach(struct hidms *ms, int flags) +{ + int rv = 0; + + DPRINTF(("hidms_detach: ms=%p flags=%d\n", ms, flags)); + + /* No need to do reference counting of hidms, wsmouse has all the goo */ + if (ms->sc_wsmousedev != NULL) + rv = config_detach(ms->sc_wsmousedev, flags); + + return (rv); +} + +void +hidms_input(struct hidms *ms, uint8_t *data, u_int len) +{ + int dx, dy, dz, dw; + u_int32_t buttons = 0; + int flags; + int i, s; + + DPRINTFN(5,("hidms_input: len=%d\n", len)); + + /* + * The Microsoft Wireless Intellimouse 2.0 sends one extra leading + * byte of data compared to most USB mice. This byte frequently + * switches from 0x01 (usual state) to 0x02. It may be used to + * report non-standard events (such as battery life). However, + * at the same time, it generates a left click event on the + * button byte, where there shouldn't be any. We simply discard + * the packet in this case. + * + * This problem affects the MS Wireless Notebook Optical Mouse, too. + * However, the leading byte for this mouse is normally 0x11, and + * the phantom mouse click occurs when it's 0x14. + */ + if (ms->sc_flags & HIDMS_LEADINGBYTE) { + if (*data++ == 0x02) + return; + /* len--; */ + } else if (ms->sc_flags & HIDMS_SPUR_BUT_UP) { + if (*data == 0x14 || *data == 0x15) + return; + } + + flags = WSMOUSE_INPUT_DELTA; + if (ms->sc_flags & HIDMS_ABSX) + flags |= WSMOUSE_INPUT_ABSOLUTE_X; + if (ms->sc_flags & HIDMS_ABSY) + flags |= WSMOUSE_INPUT_ABSOLUTE_Y; + + dx = hid_get_data(data, len, &ms->sc_loc_x); + dy = -hid_get_data(data, len, &ms->sc_loc_y); + dz = hid_get_data(data, len, &ms->sc_loc_z); + dw = hid_get_data(data, len, &ms->sc_loc_w); + + if (ms->sc_flags & HIDMS_ABSY) + dy = -dy; + if (ms->sc_flags & HIDMS_REVZ) + dz = -dz; + if (ms->sc_flags & HIDMS_REVW) + dw = -dw; + + if (ms->sc_tsscale.swapxy && !ms->sc_rawmode) { + int tmp = dx; + dx = dy; + dy = tmp; + } + + if (!ms->sc_rawmode && + (ms->sc_tsscale.maxx - ms->sc_tsscale.minx) != 0 && + (ms->sc_tsscale.maxy - ms->sc_tsscale.miny) != 0) { + /* Scale down to the screen resolution. */ + dx = ((dx - ms->sc_tsscale.minx) * ms->sc_tsscale.resx) / + (ms->sc_tsscale.maxx - ms->sc_tsscale.minx); + dy = ((dy - ms->sc_tsscale.miny) * ms->sc_tsscale.resy) / + (ms->sc_tsscale.maxy - ms->sc_tsscale.miny); + } + + for (i = 0; i < ms->sc_num_buttons; i++) + if (hid_get_data(data, len, &ms->sc_loc_btn[i])) + buttons |= (1 << HIDMS_BUT(i)); + + if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || + buttons != ms->sc_buttons) { + DPRINTFN(10, ("hidms_input: x:%d y:%d z:%d w:%d buttons:0x%x\n", + dx, dy, dz, dw, buttons)); + ms->sc_buttons = buttons; + if (ms->sc_wsmousedev != NULL) { + s = spltty(); + wsmouse_input(ms->sc_wsmousedev, buttons, + dx, dy, dz, dw, flags); + splx(s); + } + } +} + +int +hidms_enable(struct hidms *ms) +{ + if (ms->sc_enabled) + return EBUSY; + + ms->sc_enabled = 1; + ms->sc_buttons = 0; + return 0; +} + +int +hidms_ioctl(struct hidms *ms, u_long cmd, caddr_t data, int flag, + struct proc *p) +{ + struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; + + switch (cmd) { + case WSMOUSEIO_SCALIBCOORDS: + if (!(wsmc->minx >= 0 && wsmc->maxx >= 0 && + wsmc->miny >= 0 && wsmc->maxy >= 0 && + wsmc->resx >= 0 && wsmc->resy >= 0 && + wsmc->minx < 32768 && wsmc->maxx < 32768 && + wsmc->miny < 32768 && wsmc->maxy < 32768 && + (wsmc->maxx - wsmc->minx) != 0 && + (wsmc->maxy - wsmc->miny) != 0 && + wsmc->resx < 32768 && wsmc->resy < 32768 && + wsmc->swapxy >= 0 && wsmc->swapxy <= 1 && + wsmc->samplelen >= 0 && wsmc->samplelen <= 1)) + return (EINVAL); + + ms->sc_tsscale.minx = wsmc->minx; + ms->sc_tsscale.maxx = wsmc->maxx; + ms->sc_tsscale.miny = wsmc->miny; + ms->sc_tsscale.maxy = wsmc->maxy; + ms->sc_tsscale.swapxy = wsmc->swapxy; + ms->sc_tsscale.resx = wsmc->resx; + ms->sc_tsscale.resy = wsmc->resy; + ms->sc_rawmode = wsmc->samplelen; + return 0; + case WSMOUSEIO_GCALIBCOORDS: + wsmc->minx = ms->sc_tsscale.minx; + wsmc->maxx = ms->sc_tsscale.maxx; + wsmc->miny = ms->sc_tsscale.miny; + wsmc->maxy = ms->sc_tsscale.maxy; + wsmc->swapxy = ms->sc_tsscale.swapxy; + wsmc->resx = ms->sc_tsscale.resx; + wsmc->resy = ms->sc_tsscale.resy; + wsmc->samplelen = ms->sc_rawmode; + return 0; + case WSMOUSEIO_GTYPE: + if (ms->sc_flags & HIDMS_ABSX && ms->sc_flags & HIDMS_ABSY) { + *(u_int *)data = WSMOUSE_TYPE_TPANEL; + return 0; + } + /* FALLTHROUGH */ + default: + return -1; + } +} + +void +hidms_disable(struct hidms *ms) +{ + ms->sc_enabled = 0; +} diff --git a/sys/dev/hid/hidmsvar.h b/sys/dev/hid/hidmsvar.h new file mode 100644 index 00000000000..355cb8b553d --- /dev/null +++ b/sys/dev/hid/hidmsvar.h @@ -0,0 +1,83 @@ +/* $OpenBSD: hidmsvar.h,v 1.1 2016/01/08 15:54:13 jcs Exp $ */ +/* $NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define MAX_BUTTONS 31 /* must not exceed size of sc_buttons */ + +struct tsscale { + int minx, maxx; + int miny, maxy; + int swapxy; + int resx, resy; +}; + +struct hidms { + int sc_enabled; + int sc_flags; /* device configuration */ +#define HIDMS_SPUR_BUT_UP 0x001 /* spurious button up events */ +#define HIDMS_Z 0x002 /* Z direction available */ +#define HIDMS_REVZ 0x004 /* Z-axis is reversed */ +#define HIDMS_W 0x008 /* W direction available */ +#define HIDMS_REVW 0x010 /* W-axis is reversed */ +#define HIDMS_LEADINGBYTE 0x020 /* Unknown leading byte */ +#define HIDMS_ABSX 0x040 /* X-axis is absolute */ +#define HIDMS_ABSY 0x080 /* Y-axis is absolute */ +#define HIDMS_TIP 0x100 /* Tip switch on a digitiser pen */ +#define HIDMS_BARREL 0x200 /* Barrel switch on a digitiser pen */ +#define HIDMS_ERASER 0x400 /* Eraser switch on a digitiser pen */ +#define HIDMS_MS_BAD_CLASS 0x800 /* Mouse doesn't identify properly */ + + int sc_num_buttons; + u_int32_t sc_buttons; /* mouse button status */ + + struct device *sc_device; + struct device *sc_wsmousedev; + + /* locators */ + struct hid_location sc_loc_x; + struct hid_location sc_loc_y; + struct hid_location sc_loc_z; + struct hid_location sc_loc_w; + struct hid_location sc_loc_btn[MAX_BUTTONS]; + + struct tsscale sc_tsscale; + int sc_rawmode; +}; + +void hidms_attach(struct hidms *, const struct wsmouse_accessops *); +int hidms_detach(struct hidms *, int); +void hidms_disable(struct hidms *); +int hidms_enable(struct hidms *); +void hidms_input(struct hidms *, uint8_t *, u_int); +int hidms_ioctl(struct hidms *, u_long, caddr_t, int, struct proc *); +int hidms_setup(struct device *, struct hidms *, uint32_t, int, void *, + int); -- cgit v1.2.3