Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1845,6 +1845,7 @@ dev/iicbus/iicbb_if.m optional iicbb dev/iicbus/iicbus.c optional iicbus dev/iicbus/iicbus_if.m optional iicbus +dev/iicubs/iichid.c optional iichid acpi iicbus dev/iicbus/iiconf.c optional iicbus dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" @@ -3275,6 +3276,7 @@ dev/usb/usb_generic.c optional usb dev/usb/usb_handle_request.c optional usb dev/usb/usb_hid.c optional usb +dev/usb/hid.c optional usb dev/usb/usb_hub.c optional usb dev/usb/usb_if.m optional usb dev/usb/usb_lookup.c optional usb @@ -3386,6 +3388,7 @@ # dev/usb/input/atp.c optional atp dev/usb/input/uep.c optional uep +dev/usb/hid.c optional ukbd | uhid dev/usb/input/uhid.c optional uhid dev/usb/input/uhid_snes.c optional uhid_snes dev/usb/input/ukbd.c optional ukbd Index: sys/dev/iicbus/input/iichid.h =================================================================== --- /dev/null +++ sys/dev/iicbus/input/iichid.h @@ -0,0 +1,188 @@ +/* $OpenBSD: iichid.h,v 1.4 2016/01/31 18:24:35 jcs Exp $ */ +/* + * HID-over-i2c driver + * + * Copyright (c) 2015, 2016 joshua stein + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _IIC_HID_H_ +#define _IIC_HID_H_ + +#include +#include +#include + +/* from usbdi.h: Match codes. */ +/* First five codes is for a whole device. */ +#define IMATCH_VENDOR_PRODUCT_REV 14 +#define IMATCH_VENDOR_PRODUCT 13 +#define IMATCH_VENDOR_DEVCLASS_DEVPROTO 12 +#define IMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO 11 +#define IMATCH_DEVCLASS_DEVSUBCLASS 10 +/* Next six codes are for interfaces. */ +#define IMATCH_VENDOR_PRODUCT_REV_CONF_IFACE 9 +#define IMATCH_VENDOR_PRODUCT_CONF_IFACE 8 +#define IMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO 7 +#define IMATCH_VENDOR_IFACESUBCLASS 6 +#define IMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO 5 +#define IMATCH_IFACECLASS_IFACESUBCLASS 4 +#define IMATCH_IFACECLASS 3 +#define IMATCH_IFACECLASS_GENERIC 2 +/* Generic driver */ +#define IMATCH_GENERIC 1 +/* No match */ +#define IMATCH_NONE 0 + +#define IHIDBUSCF_REPORTID 0 +#define IHIDBUSCF_REPORTID_DEFAULT -1 + +#define iichidcf_reportid cf_loc[IHIDBUSCF_REPORTID] +#define IHIDEV_UNK_REPORTID IHIDBUSCF_REPORTID_DEFAULT + +#define I2C_HID_REPORT_TYPE_INPUT 0x1 +#define I2C_HID_REPORT_TYPE_OUTPUT 0x2 +#define I2C_HID_REPORT_TYPE_FEATURE 0x3 + +/* 7.2 */ +enum { + I2C_HID_CMD_DESCR = 0x0, + I2C_HID_CMD_RESET = 0x1, + I2C_HID_CMD_GET_REPORT = 0x2, + I2C_HID_CMD_SET_REPORT = 0x3, + I2C_HID_CMD_GET_IDLE = 0x4, + I2C_HID_CMD_SET_IDLE = 0x5, + I2C_HID_CMD_GET_PROTO = 0x6, + I2C_HID_CMD_SET_PROTO = 0x7, + I2C_HID_CMD_SET_POWER = 0x8, + + /* pseudo commands */ + I2C_HID_REPORT_DESCR = 0x100, +}; + +#define I2C_HID_POWER_ON = 0x0; +#define I2C_HID_POWER_OFF = 0x1; + +/* 5.1.1 - HID Descriptor Format */ +struct i2c_hid_desc { + uint16_t wHIDDescLength; + uint16_t bcdVersion; + uint16_t wReportDescLength; + uint16_t wReportDescRegister; + uint16_t wInputRegister; + uint16_t wMaxInputLength; + uint16_t wOutputRegister; + uint16_t wMaxOutputLength; + uint16_t wCommandRegister; + uint16_t wDataRegister; + uint16_t wVendorID; + uint16_t wProductID; + uint16_t wVersionID; + uint32_t reserved; +} __packed; + +#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) +#define MOUSE_FLAGS (HIO_RELATIVE) + +#define MS_BUF_SIZE 8 /* bytes */ +#define MS_BUFQ_MAXLEN 100 /* units */ +#define MS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ +#define MS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) +#define MS_INFO_MAX 2 /* maximum number of HID sets */ + +struct ms_info { + struct hid_location sc_loc_w; + struct hid_location sc_loc_x; + struct hid_location sc_loc_y; + struct hid_location sc_loc_z; + struct hid_location sc_loc_t; + struct hid_location sc_loc_btn[MS_BUTTON_MAX]; + + uint32_t sc_flags; +#define MS_FLAG_X_AXIS 0x0001 +#define MS_FLAG_Y_AXIS 0x0002 +#define MS_FLAG_Z_AXIS 0x0004 +#define MS_FLAG_T_AXIS 0x0008 +#define MS_FLAG_SBU 0x0010 /* spurious button up events */ +#define MS_FLAG_REVZ 0x0020 /* Z-axis is reversed */ +#define MS_FLAG_W_AXIS 0x0040 + + uint8_t sc_iid_w; + uint8_t sc_iid_x; + uint8_t sc_iid_y; + uint8_t sc_iid_z; + uint8_t sc_iid_t; + uint8_t sc_iid_btn[MS_BUTTON_MAX]; + uint8_t sc_buttons; +}; + +struct iichid_hw { + uint8_t device_addr; + uint16_t config_reg; +}; + +struct ms_tx_entry { + STAILQ_ENTRY(ms_tx_entry) next; + uint8_t buf[MS_BUF_SIZE]; +}; + +STAILQ_HEAD(ms_tx_buf, ms_tx_entry); + +struct iichid_softc { + device_t dev; + struct cdev *cdev; + bool isopen; + struct ms_tx_entry rbuf; + uint8_t bytesread; + + struct ms_tx_buf ms_unused_blocks; + struct ms_tx_buf ms_queue; + + struct iichid_hw hw; + + driver_intr_t* intr_handler; + + struct i2c_hid_desc desc; + struct ms_info info[MS_INFO_MAX]; + uint8_t sc_iid; + mousehw_t sc_hw; + mousestatus_t sc_status; + mousemode_t sc_mode; + struct mtx lock; + + struct cv cv; + bool detaching; + + uint8_t *input_buf; + int input_size; +}; + +struct iichid_acpi_softc { + device_t dev; + struct cdev *sc_devnode; + + struct iichid_hw hw; + + uint16_t irq; + int irq_rid; + struct resource* irq_res; + void* irq_cookie; + + struct mtx lock; +}; + +int iichid_acpi_get_report(device_t dev, struct i2c_hid_desc* hid_desc, enum hid_kind type, int id, void *data, int len); + + +#endif /* _IIC_HID_H_ */ Index: sys/dev/iicbus/input/iichid.c =================================================================== --- /dev/null +++ sys/dev/iicbus/input/iichid.c @@ -0,0 +1,795 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iicbus_if.h" + +static device_probe_t iichid_probe; +static device_attach_t iichid_attach; +static device_detach_t iichid_detach; + +static devclass_t iichid_devclass; + +static device_method_t iichid_methods[] = { + DEVMETHOD(device_probe, iichid_probe), + DEVMETHOD(device_attach, iichid_attach), + DEVMETHOD(device_detach, iichid_detach), + + DEVMETHOD_END +}; + +static d_open_t iichid_open; +static d_close_t iichid_close; +static d_read_t iichid_read; +static d_write_t iichid_write; +static d_ioctl_t iichid_ioctl; + +static struct cdevsw iichid_cdevsw = { + .d_version = D_VERSION, + .d_open = iichid_open, + .d_close = iichid_close, + .d_read = iichid_read, + .d_write = iichid_write, + .d_ioctl = iichid_ioctl, + .d_name = "iichid" +}; + +static driver_t iichid_driver = { + .name = "iichid", + .methods = iichid_methods, + .size = sizeof(struct iichid_softc), +}; + +static int +iichid_fetch_buffer(device_t dev, uint8_t* cmd, int cmdlen, uint8_t *buf, int buflen) +{ + uint16_t addr = iicbus_get_addr(dev); + struct iic_msg msgs[] = { + { addr << 1, IIC_M_WR | IIC_M_NOSTOP, cmdlen, cmd }, + { addr << 1, IIC_M_RD, buflen, buf }, + }; + + return (iicbus_transfer(dev, msgs, nitems(msgs))); +} + +static int +my_iichid_get_report(device_t dev, struct i2c_hid_desc* hid_desc, enum hid_kind type, int id, uint8_t *data, int len, int *actual_len) +{ + int t = -1; + *actual_len = 0; + switch (type) { + case hid_input: + t = I2C_HID_REPORT_TYPE_INPUT; + break; + case hid_output: + t = I2C_HID_REPORT_TYPE_OUTPUT; + break; + case hid_feature: + t = I2C_HID_REPORT_TYPE_FEATURE; + break; + default: + t = -1; + break; + } + + if (t<0) + return (EINVAL); + + uint16_t dtareg = htole16(hid_desc->wDataRegister); + uint16_t cmdreg = htole16(hid_desc->wCommandRegister); + + uint8_t cmd[] = { /*____________|______id>=15______|______id<15______*/ + cmdreg & 0xff , + cmdreg >> 8 , + (id >= 15 ? 15 | ( t << 4 ) : id | ( t << 4 ) ), + I2C_HID_CMD_GET_REPORT , + (id >= 15 ? id : dtareg & 0xff ), + (id >= 15 ? dtareg & 0xff : dtareg << 8 ), + (id >= 15 ? dtareg << 8 : 0 ), + }; + int cmdlen = (id >= 15 ? 7 : 6 ); + + int error = iichid_fetch_buffer(dev, cmd, cmdlen, data, len); + + if (error != 0) + { + device_printf(dev, "could not retrieve input report (%d)\n", error); + return error; + } + + *actual_len = data[0] | data[1] << 8; + + return 0; +} + +static int +iichid_fetch_report(device_t dev, struct i2c_hid_desc* hid_desc, uint8_t *data, int len, int *actual_len) +{ + *actual_len = 0; + + uint16_t dtareg = htole16(hid_desc->wInputRegister); + + uint8_t cmd[] = {dtareg & 0xff, dtareg >> 8}; + int cmdlen = 2; + + int error = iichid_fetch_buffer(dev, cmd, cmdlen, data, len); + + if (error != 0) + { + device_printf(dev, "could not retrieve input report (%d)\n", error); + return error; + } + + *actual_len = data[0] | data[1] << 8; + + return 0; +} + +static int fetch_hid_descriptor(device_t dev) +{ + struct iichid_softc *sc = device_get_softc(dev); + + uint16_t cr = sc->hw.config_reg; + return (iichid_fetch_buffer(dev, (uint8_t*)&cr, sizeof(cr), (uint8_t*)&sc->desc, sizeof(struct i2c_hid_desc))); +} + +static int fetch_report_descriptor(device_t dev, uint8_t **buf, int *len) +{ + struct iichid_softc *sc = device_get_softc(dev); + + if (sc->desc.wHIDDescLength != 30) + return -1; + + *buf = malloc(sc->desc.wReportDescLength, M_TEMP, M_NOWAIT | M_ZERO); + *len = sc->desc.wReportDescLength; + + uint16_t rdr = sc->desc.wReportDescRegister; + + int error = (iichid_fetch_buffer(dev, (uint8_t*)&rdr, sizeof(rdr), *buf, sc->desc.wReportDescLength)); + + return error; +} + +static void +ms_hid_parse(device_t dev, const uint8_t *buf, uint16_t len, struct ms_info *info, uint8_t index) +{ + uint32_t flags; + uint8_t i; + uint8_t j; + + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_X_AXIS; + } + } + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), + hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_Y_AXIS; + } + } + /* Try the wheel first as the Z activator since it's tradition. */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags, + &info->sc_iid_z) || + hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags, + &info->sc_iid_z)) { + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_Z_AXIS; + } + /* + * We might have both a wheel and Z direction, if so put + * put the Z on the W coordinate. + */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_Z), hid_input, index, &info->sc_loc_w, &flags, + &info->sc_iid_w)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_W_AXIS; + } + } + } else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_Z), hid_input, index, &info->sc_loc_z, &flags, + &info->sc_iid_z)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_Z_AXIS; + } + } + /* + * The Microsoft Wireless Intellimouse 2.0 reports it's wheel + * using 0x0048, which is HUG_TWHEEL, and seems to expect you + * 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 + */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_TWHEEL), hid_input, index, &info->sc_loc_t, + &flags, &info->sc_iid_t)) { + + info->sc_loc_t.pos += 8; + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_T_AXIS; + } + } else if (hid_locate(buf, len, HID_USAGE2(HUP_CONSUMER, + HUC_AC_PAN), hid_input, index, &info->sc_loc_t, + &flags, &info->sc_iid_t)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) + info->sc_flags |= MS_FLAG_T_AXIS; + } + /* figure out the number of buttons */ + + for (i = 0; i < MS_BUTTON_MAX; i++) { + if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)), + hid_input, index, &info->sc_loc_btn[i], NULL, + &info->sc_iid_btn[i])) { + break; + } + } + + /* detect other buttons */ + + for (j = 0; (i < MS_BUTTON_MAX) && (j < 2); i++, j++) { + if (!hid_locate(buf, len, HID_USAGE2(HUP_MICROSOFT, (j + 1)), + hid_input, index, &info->sc_loc_btn[i], NULL, + &info->sc_iid_btn[i])) { + break; + } + } + + info->sc_buttons = i; + + if (info->sc_flags == 0) + return; + + /* announce information about the mouse */ + device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n", + (info->sc_buttons), + (info->sc_flags & MS_FLAG_X_AXIS) ? "X" : "", + (info->sc_flags & MS_FLAG_Y_AXIS) ? "Y" : "", + (info->sc_flags & MS_FLAG_Z_AXIS) ? "Z" : "", + (info->sc_flags & MS_FLAG_T_AXIS) ? "T" : "", + (info->sc_flags & MS_FLAG_W_AXIS) ? "W" : "", + info->sc_iid_x); +} + +static int +iichid_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct iichid_softc *sc = dev->si_drv1; + + mtx_lock(&sc->lock); + if (sc->isopen) + { + mtx_unlock(&sc->lock); + return (EBUSY); + } + + sc->isopen = true; + sc->bytesread = sc->sc_mode.packetsize; + + + while (!STAILQ_EMPTY(&sc->ms_queue)) + { + struct ms_tx_entry *u = STAILQ_FIRST(&sc->ms_queue); + STAILQ_REMOVE_HEAD(&sc->ms_queue, next); + STAILQ_INSERT_HEAD(&sc->ms_unused_blocks, u, next); + } + + mtx_unlock(&sc->lock); + + return 0; +} + +static int +iichid_close(struct cdev *dev, int fflags, int devtype, struct thread *td) +{ + struct iichid_softc *sc = dev->si_drv1; + + cv_broadcastpri(&sc->cv, 0); + + mtx_lock(&sc->lock); + sc->isopen=false; + mtx_unlock(&sc->lock); + + return 0; +} + +static int +iichid_write(struct cdev *dev, struct uio *uio, int ioflags) +{ + return 0; +} + +static int +iichid_read(struct cdev *dev, struct uio* uio, int ioflags) +{ + struct iichid_softc *sc = dev->si_drv1; + + mtx_lock(&sc->lock); + + while (!sc->detaching && STAILQ_EMPTY(&sc->ms_queue) && sc->bytesread == sc->sc_mode.packetsize) + { + int error = cv_wait_sig(&sc->cv, &sc->lock); + if (error != 0) + { + mtx_unlock(&sc->lock); + return error; + } + } + + if (sc->detaching) + { + mtx_unlock(&sc->lock); + return ENXIO; + } + + if (sc->bytesread == sc->sc_mode.packetsize && !STAILQ_EMPTY(&sc->ms_queue)) + { + struct ms_tx_entry *u = STAILQ_FIRST(&sc->ms_queue); + STAILQ_REMOVE_HEAD(&sc->ms_queue, next); + memcpy(&sc->rbuf, u, sizeof(struct ms_tx_entry)); + sc->bytesread = 0; + + STAILQ_INSERT_TAIL(&sc->ms_unused_blocks, u, next); + } + mtx_unlock(&sc->lock); + + int error = uiomove(sc->rbuf.buf+sc->bytesread, 1, uio); + sc->bytesread++; + if (error != 0) + device_printf(sc->dev, "I could not be read from"); + + return 0; +} + +static int +iichid_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +{ + struct iichid_softc *sc = dev->si_drv1; + int error = 0; + mousemode_t mode; + + mtx_lock(&sc->lock); + + switch (cmd) { + case MOUSE_SETMODE: + mode = *(mousemode_t*)data; + + if (mode.level == -1) { + /* don't change the current setting */ + } else if ((mode.level < 0) || (mode.level > 1)) { + error = EINVAL; + break; + } else { + sc->sc_mode.level = mode.level; + } + + if (sc->sc_mode.level == 0) { + if (sc->sc_hw.buttons > MOUSE_MSC_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + } else if (sc->sc_mode.level == 1) { + if (sc->sc_hw.buttons > MOUSE_SYS_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; + } + break; + case MOUSE_SETLEVEL: + if (*(int *)data < 0 || *(int *)data > 1) { + error = EINVAL; + break; + } + sc->sc_mode.level = *(int *)data; + + if (sc->sc_mode.level == 0) { + if (sc->sc_hw.buttons > MOUSE_MSC_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + } else if (sc->sc_mode.level == 1) { + if (sc->sc_hw.buttons > MOUSE_SYS_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; + } + break; + case MOUSE_GETHWINFO: + *(mousehw_t *)data = sc->sc_hw; + break; + + case MOUSE_GETMODE: + *(mousemode_t *)data = sc->sc_mode; + break; + + case MOUSE_GETLEVEL: + *(int *)data = sc->sc_mode.level; + break; + + case MOUSE_GETSTATUS:{ + mousestatus_t *status = (mousestatus_t *)data; + + *status = sc->sc_status; + sc->sc_status.obutton = sc->sc_status.button; + sc->sc_status.button = 0; + sc->sc_status.dx = 0; + sc->sc_status.dy = 0; + sc->sc_status.dz = 0; + /* sc->sc_status.dt = 0; */ + + if (status->dx || status->dy || status->dz /* || status->dt */ ) { + status->flags |= MOUSE_POSCHANGED; + } + if (status->button != status->obutton) { + status->flags |= MOUSE_BUTTONSCHANGED; + } + break; + } + default: + error = ENOTTY; + break; + } + + mtx_unlock(&sc->lock); + return (error); +} + +static void +ms_put_queue(struct iichid_softc *sc, int32_t dx, int32_t dy, + int32_t dz, int32_t dt, int32_t buttons) +{ + if (dx > 254) + dx = 254; + if (dx < -256) + dx = -256; + if (dy > 254) + dy = 254; + if (dy < -256) + dy = -256; + if (dz > 126) + dz = 126; + if (dz < -128) + dz = -128; + if (dt > 126) + dt = 126; + if (dt < -128) + dt = -128; + + mtx_lock(&sc->lock); + if (!STAILQ_EMPTY(&sc->ms_unused_blocks)) + { + struct ms_tx_entry *u = STAILQ_FIRST(&sc->ms_unused_blocks); + STAILQ_REMOVE_HEAD(&sc->ms_unused_blocks, next); + + u->buf[0] = MOUSE_MSC_SYNC; + u->buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; + u->buf[1] = dx >> 1; + u->buf[2] = dy >> 1; + u->buf[3] = dx - (dx >> 1); + u->buf[4] = dy - (dy >> 1); + + if (sc->sc_mode.level == 1) { + u->buf[5] = dz >> 1; + u->buf[6] = dz - (dz >> 1); + u->buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); + } + + STAILQ_INSERT_TAIL(&sc->ms_queue, u, next); + } else { + device_printf(sc->dev, "no blocks available\n"); + } + mtx_unlock(&sc->lock); + + cv_signal(&sc->cv); +} +static void +iichid_intr(void* context) +{ + struct iichid_softc *sc = context; + + int actual = 0; + int error = iichid_fetch_report(sc->dev, &sc->desc, sc->input_buf, sc->input_size, &actual); + + if (error != 0) + { + device_printf(sc->dev, "an error occured\n"); + return; + } + + if (actual <= 0) + { + device_printf(sc->dev, "no data received\n"); + return; + } + + int32_t dw = 0; + int32_t dx = 0; + int32_t dy = 0; + int32_t dz = 0; + int32_t dt = 0; + int32_t buttons = 0; + int32_t buttons_found = 0; + uint8_t id = 0; + + uint8_t *buf = sc->input_buf; + buf++; buf++; + int len = actual; + + if (sc->sc_iid) + { + id = *buf; + buf++; + len--; + } + +// device_printf(sc->dev, "id: %d\n", id); + + for(int i=0; iinfo[i]; + if ((info->sc_flags & MS_FLAG_W_AXIS) && + (id == info->sc_iid_w)) + dw += hid_get_data(buf, len, &info->sc_loc_w); + + if ((info->sc_flags & MS_FLAG_X_AXIS) && + (id == info->sc_iid_x)) + dx += hid_get_data(buf, len, &info->sc_loc_x); + + if ((info->sc_flags & MS_FLAG_Y_AXIS) && + (id == info->sc_iid_y)) + dy -= hid_get_data(buf, len, &info->sc_loc_y); + + if ((info->sc_flags & MS_FLAG_Z_AXIS) && + (id == info->sc_iid_z)) { + int32_t temp; + temp = hid_get_data(buf, len, &info->sc_loc_z); + dz -= temp; + } + + if ((info->sc_flags & MS_FLAG_T_AXIS) && + (id == info->sc_iid_t)) { + dt -= hid_get_data(buf, len, &info->sc_loc_t); + /* T-axis is translated into button presses */ + buttons_found |= (1UL << 5) | (1UL << 6); + } + + for (i = 0; i < info->sc_buttons; i++) { + uint32_t mask; + mask = 1UL << MS_BUT(i); + /* check for correct button ID */ + if (id != info->sc_iid_btn[i]) + continue; + /* check for button pressed */ + if (hid_get_data(buf, len, &info->sc_loc_btn[i])) + buttons |= mask; + /* register button mask */ + buttons_found |= mask; + } + + buttons |= sc->sc_status.button & ~buttons_found; + + if (dx || dy || dz || dt || dw || + (buttons != sc->sc_status.button)) { + + /* translate T-axis into button presses until further */ + if (dt > 0) { + ms_put_queue(sc, 0, 0, 0, 0, buttons); + buttons |= 1UL << 5; + } else if (dt < 0) { + ms_put_queue(sc, 0, 0, 0, 0, buttons); + buttons |= 1UL << 6; + } + + sc->sc_status.button = buttons; + sc->sc_status.dx += dx; + sc->sc_status.dy += dy; + sc->sc_status.dz += dz; + + //device_printf(sc->dev, "dx: %d, dy: %d, dz: %d, dt: %d, dw: %d, btn: 0x%2x\n", dx, dy, dz, dt, dw, buttons); + + ms_put_queue(sc, dx, dy, dz, dt, buttons); + } + } + +// device_printf(sc->dev, "read: %d/%d\n", actual, sc->desc.wMaxInputLength); +} + +static int +iichid_probe(device_t dev) +{ + device_t pdev = device_get_parent(dev); + + if (!pdev) + return (ENXIO); + + driver_t *pdrv = device_get_driver(pdev); + + if (!pdrv) + return (ENXIO); + + if (strcmp(pdrv->name, "iicbus") != 0) + return (ENXIO); + + device_set_desc(dev, "HID over I2C"); + + return (BUS_PROBE_VENDOR); +} + +static int +iichid_attach(device_t dev) +{ + struct iichid_softc *sc = device_get_softc(dev); + + uintptr_t addr = 0, cr = 0; + int error; + + sc->dev = dev; + sc->detaching = false; + sc->input_buf = NULL; + sc->isopen = 0; + + // the config register is passed as resources, while the device address will always have a place in the iicbus child's (ivars) heart + bus_get_resource(dev, SYS_RES_IOPORT, 0, (rman_res_t*)&cr, NULL); + BUS_READ_IVAR(device_get_parent(dev), dev, IICBUS_IVAR_ADDR, &addr); + + // store the value in device's softc to have easy access + // values only have 1 byte length, still make the casts explicit + sc->hw.device_addr = (uint8_t)addr; + sc->hw.config_reg = (uint16_t)cr; + + sc->intr_handler = iichid_intr; + + sc->cdev = make_dev(&iichid_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ims%d", device_get_unit(dev)); + sc->cdev->si_drv1 = sc; + + device_printf(dev, "ADDR 0x%x REG 0x%x\n", sc->hw.device_addr, sc->hw.config_reg); + + if ( (error = fetch_hid_descriptor(dev)) != 0 ) + { + iichid_detach(dev); + device_printf(dev, "could not retrieve HID descriptor from device: %d\n", error); + } + + uint8_t *rdesc; + int len; + + if ( (error = fetch_report_descriptor(dev, &rdesc, &len)) != 0 ) + { + iichid_detach(dev); + device_printf(dev, "could not retrieve report descriptor from device: %d\n", error); + } + + sc->input_size = sc->desc.wMaxInputLength; + sc->input_buf = malloc(sc->desc.wMaxInputLength, M_DEVBUF, M_NOWAIT | M_ZERO); + + for (int i = 0; i < MS_INFO_MAX; i++) { + ms_hid_parse(dev, rdesc, len, &sc->info[0], 0); + } + + int isize = hid_report_size(rdesc, len, hid_input, &sc->sc_iid); + + if (isize != sc->desc.wMaxInputLength) + device_printf(dev, "determined (len=%d) and described (len=%d) input report lengths mismatch\n", isize, sc->desc.wMaxInputLength); + + mtx_init(&sc->lock, "iichid spin-lock", NULL, MTX_DEF); + cv_init(&sc->cv, "iichid cv"); + + sc->sc_hw.buttons = -1; + sc->sc_hw.iftype = MOUSE_IF_SYSMOUSE; + sc->sc_hw.type = MOUSE_MOUSE; + sc->sc_hw.model = MOUSE_MODEL_GENERIC; + sc->sc_hw.hwid = sc->desc.wVendorID; + + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.rate = -1; + sc->sc_mode.resolution = -1; + sc->sc_mode.accelfactor = 1; + sc->sc_mode.level = 0; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + + STAILQ_INIT(&sc->ms_queue); + STAILQ_INIT(&sc->ms_unused_blocks); + for(int i=0; ims_unused_blocks, u, next); + } + +// device_printf(dev, "len: %d\nbcdVer: %d\nreport len: %d\ninput len: %d\nvid: 0x%x\npid: 0x%x\n", hid_desc.wHIDDescLength, hid_desc.bcdVersion, hid_desc.wReportDescLength, hid_desc.wMaxInputLength, hid_desc.wVendorID, hid_desc.wProductID); + + return (0); /* success */ +} + +static int +iichid_detach(device_t dev) +{ + struct iichid_softc *sc = device_get_softc(dev); + if (sc) + { + if (sc->isopen) + return (EBUSY); + + mtx_lock(&sc->lock); + sc->detaching = true; + mtx_unlock(&sc->lock); + cv_broadcastpri(&sc->cv,0); + if (mtx_initialized(&sc->lock)) + { + mtx_destroy(&sc->lock); + } + + struct ms_tx_buf* queues[2] = {&sc->ms_queue, &sc->ms_unused_blocks}; + for(int i=0; i<2; i++) + { + while (!STAILQ_EMPTY(queues[i])) + { + struct ms_tx_entry *u = STAILQ_FIRST(queues[i]); + STAILQ_REMOVE_HEAD(queues[i], next); + free(u, M_DEVBUF); + } + } + + if (sc->cdev) + destroy_dev(sc->cdev); + + if (sc->input_buf) + free(sc->input_buf, M_DEVBUF); + } + return (0); +} + +DRIVER_MODULE(iichid, iicbus, iichid_driver, iichid_devclass, NULL, 0); +MODULE_DEPEND(iichid, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); +MODULE_VERSION(iichid, 1); Index: sys/dev/iicbus/input/iichid_acpi.c =================================================================== --- /dev/null +++ sys/dev/iicbus/input/iichid_acpi.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +static device_probe_t iichid_acpi_probe; +static device_attach_t iichid_acpi_attach; +static device_detach_t iichid_acpi_detach; + +static devclass_t iichid_acpi_devclass; + +static device_method_t iichid_acpi_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, iichid_acpi_probe), + DEVMETHOD(device_attach, iichid_acpi_attach), + DEVMETHOD(device_detach, iichid_acpi_detach), + + DEVMETHOD_END +}; + +static driver_t iichid_acpi_driver = { + .name = "iichid_acpi", + .methods = iichid_acpi_methods, + .size = sizeof(struct iichid_acpi_softc), +}; + +static char *iichid_acpi_ids[] = { + "PNP0C50", + "ACPI0C50", + NULL +}; + +static ACPI_STATUS +iichid_acpi_walk_handler(ACPI_RESOURCE *res, void *context) +{ + struct iichid_acpi_softc *sc; + + sc = context; + + switch(res->Type) { + case ACPI_RESOURCE_TYPE_SERIAL_BUS: + //device_printf(sc->dev, " - serial bus: "); + if (res->Data.CommonSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { + device_printf(sc->dev, "wrong bus type, should be %d is %d\n", ACPI_RESOURCE_SERIAL_TYPE_I2C, res->Data.CommonSerialBus.Type); + return (AE_TYPE); + } else { + //device_printf(sc->dev, "0x%x on %s\n", le16toh(res->Data.I2cSerialBus.SlaveAddress), res->Data.CommonSerialBus.ResourceSource.StringPtr); + sc->hw.device_addr = res->Data.I2cSerialBus.SlaveAddress; + } + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + if (res->Data.ExtendedIrq.InterruptCount > 0) { + //device_printf(sc->dev, " - irq: %d\n", (int)res->Data.ExtendedIrq.Interrupts[0]); + sc->irq = res->Data.ExtendedIrq.Interrupts[0]; + } + + break; + case ACPI_RESOURCE_TYPE_END_TAG: + //device_printf(sc->dev, " - end parsing\n"); + break; + + default: + device_printf(sc->dev, "unexpected type %d while parsing Current Resource Settings (_CSR)\n", res->Type); + break; + } + + return AE_OK; +} + +static int +iichid_acpi_probe(device_t dev) +{ + if (acpi_disabled("iichid") || ACPI_ID_PROBE(device_get_parent(dev), dev, iichid_acpi_ids) == NULL) + return (ENXIO); + + device_set_desc(dev, "HID over I2C (ACPI)"); + + return (BUS_PROBE_VENDOR); +} + +static int +iichid_acpi_attach(device_t dev) +{ + struct iichid_acpi_softc *sc; + sc = device_get_softc(dev); + + mtx_init(&sc->lock, "HID over I2C (ACPI) lock", NULL, MTX_DEF); + + sc->dev = dev; + + //get ACPI handles for current device and its parent + ACPI_HANDLE ahnd = acpi_get_handle(dev), + phnd = NULL; + + if (!ahnd) { + device_printf(dev, "Could not retrieve ACPI handle\n"); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //besides the ACPI parent handle, get the newbus parent + device_t parent; + + if (ACPI_SUCCESS(AcpiGetParent(ahnd, &phnd)) && (parent = acpi_get_device(phnd)) && device_is_attached(parent)) + { + //device_printf(dev, "my parent is a %s and its ACPI path is %s\n", device_get_driver(parent)->name, acpi_name(phnd)); + } else { + device_printf(dev, "could not retrieve parent device or parent is not attached (driver loaded?)"); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + + //function (_DSM) to be evaluated to retrieve the address of the configuration register of the hi device + /* 3cdff6f7-4267-4555-ad05-b30a3d8938de */ + static uint8_t iichid_acpi_dsm_guid[] = { + 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, + 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, + }; + + //prepare 4 arguments + ACPI_OBJECT obj[4]; + ACPI_OBJECT_LIST acpi_arg; + + ACPI_BUFFER acpi_buf; + + acpi_buf.Pointer = NULL; + acpi_buf.Length = ACPI_ALLOCATE_BUFFER; + + acpi_arg.Pointer = &obj[0]; + acpi_arg.Count = 4; + + obj[0].Type = ACPI_TYPE_BUFFER; + obj[0].Buffer.Length = sizeof(iichid_acpi_dsm_guid); + obj[0].Buffer.Pointer = &iichid_acpi_dsm_guid[0]; + + obj[1].Type = ACPI_TYPE_INTEGER; + obj[1].Integer.Value = 1; + + obj[2].Type = ACPI_TYPE_INTEGER; + obj[2].Integer.Value = 1; + + obj[3].Type = ACPI_TYPE_PACKAGE; + obj[3].Package.Count = 0; + + //evaluate + ACPI_STATUS status = ACPI_EVALUATE_OBJECT(device_get_parent(dev), dev, "_DSM", &acpi_arg, &acpi_buf); + + if (ACPI_FAILURE(status)) { + device_printf(dev, "error evaluating _DSM\n"); + if (acpi_buf.Pointer != NULL) + AcpiOsFree(acpi_buf.Pointer); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //the result will contain the register address (int type) + ACPI_OBJECT *result = (ACPI_OBJECT*)acpi_buf.Pointer; + if( result->Type != ACPI_TYPE_INTEGER ) { + device_printf(dev, "_DSM should return descriptor register address as integer\n"); + AcpiOsFree(result); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //take it (much work done for one byte -.-) + device_printf(dev, "descriptor register address is %lx\n", result->Integer.Value); + sc->hw.config_reg = result->Integer.Value; + + //cleanup + AcpiOsFree(result); + + //_CSR holds more data (device address and irq) and only needs a callback to evaluate its data + status = AcpiWalkResources(ahnd, "_CRS", iichid_acpi_walk_handler, sc); + + if (ACPI_FAILURE(status)) { + device_printf(dev, "could not evaluate _CRS\n"); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //get the full ACPI pathname of dev's parent + acpi_buf.Pointer = NULL; + acpi_buf.Length = ACPI_ALLOCATE_BUFFER; + AcpiGetName(phnd, ACPI_FULL_PATHNAME, &acpi_buf); + + device_printf(dev, "parent device is \"%s\"\n", (const char*)acpi_buf.Pointer); + AcpiOsFree(acpi_buf.Pointer); + + //padev will hold the newbus device handle of this (dev) devices ACPI parent, which is not necessarily the same as this (dev) devices parent. + //I.e. both parent devices might even be on different branches of the device tree. The ACPI parent is most likely a iicbus + //device (e.g. ig4's ig4iic_pci0), while the newbus parent of dev will in most cases acpi0. + device_t padev = acpi_get_device(phnd); //ACPI parent, the one we are interested in because it is a iicbus +#if 0 + device_t pbdev = device_get_parent(dev); //newbus parent, just for reference + + device_printf(dev, "parent devices: 0x%lx (ACPI, %s) and 0x%lx (Newbus, %s)\n", (uint64_t)padev, device_get_name(padev), (uint64_t)pbdev, device_get_name(padev)); +#endif + //look below padev whether there already is a iichid device that can be reused or create a new one + if (padev) + { + //the should be a iicbus device, nevertheless no KASSERT here since the system will continue to function + // only iichid device won't operate + device_t iicbus_dev = device_find_child(padev, "iicbus", -1); + if (iicbus_dev) + { + device_t *children; + int ccount; + + device_t dnew = NULL; + + //get a list of all children below iicbus and check if parameters match + if (device_get_children(iicbus_dev, &children, &ccount) == 0) + { + for(int i=0; iname, "iichid") == 0) + { + struct iichid_softc *dsc = (struct iichid_softc*)device_get_softc(children[i]); + if ( dsc->hw.device_addr == sc->hw.device_addr + && dsc->hw.config_reg == sc->hw.config_reg ) + { + //reuse this child, there shouldn't be more than one + //if there are more devices matching that is observable in dmesg + dnew = children[i]; + device_printf(dev, "device %s ADDR 0x%x REG 0x%x already present on %s\n", device_get_nameunit(children[i]), dsc->hw.device_addr, dsc->hw.config_reg, device_get_nameunit(iicbus_dev)); + } + } + } + free(children, M_TEMP); + } + + //no iichid device found to be reused, so one is created and parameters are set + if ( dnew == NULL ) + { + //add child of type iichid below iicbus + dnew = BUS_ADD_CHILD(iicbus_dev, 0, "iichid", -1); + if (dnew) + { + //config register and device address via resource_list/ivars + bus_set_resource(dnew, SYS_RES_IOPORT, 0, sc->hw.config_reg, 2); + BUS_WRITE_IVAR(iicbus_dev, dnew, IICBUS_IVAR_ADDR, sc->hw.device_addr); + + //try and attach: + if (device_probe_and_attach(dnew) == 0) + { + // success? print status and device configuration + struct iichid_softc *dsc = (struct iichid_softc*)device_get_softc(dnew); + device_printf(dev, "added %s ADDR 0x%x REG 0x%x to %s\n", device_get_nameunit(dnew), dsc->hw.device_addr, dsc->hw.config_reg, device_get_nameunit(iicbus_dev)); + } else { + //failure? remove child, print error and leave + device_printf(dev, "probe or attach failed for %s! (ADDR: 0x%x, REG: 0x%x)\n", device_get_nameunit(dnew), sc->hw.device_addr, sc->hw.config_reg); + device_delete_child(iicbus_dev, dnew); + mtx_destroy(&sc->lock); + return (ENXIO); + } + } else { + device_printf(dev, "could not attach iichid device to %s! (ADDR: 0x%x, REG: 0x%x)\n", device_get_nameunit(iicbus_dev), sc->hw.device_addr, sc->hw.config_reg); + mtx_destroy(&sc->lock); + return (ENXIO); + } + } + + if ( dnew != NULL ) + { + struct iichid_softc *dsc = (struct iichid_softc*)device_get_softc(dnew); + if (dsc && dsc->intr_handler) + { + sc->irq_rid = 0; + sc->irq_cookie = 0; + sc->irq_res = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE); + device_printf(dev, "allocated irq at 0x%lx and rid %d\n", (uint64_t)sc->irq_res, sc->irq_rid); + + if( sc->irq_res != NULL ) + { + int error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_TTY | INTR_MPSAFE, NULL, dsc->intr_handler, dsc, &sc->irq_cookie); + if (error != 0) + { + device_printf(dev, "Could not setup interrupt handler\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); + return error; + } else + device_printf(dev, "successfully setup interrupt\n"); + + } else { + device_printf(dev, "could not allocate IRQ resource\n"); + } + } + } + } + } + + + + return (0); /* success */ +} + +static int +iichid_acpi_detach(device_t dev) +{ + //we leave the added devices below iicbus instances intact, since this module is only needed to parameterize + // them. Afterwards they function without this + struct iichid_acpi_softc *sc; + sc = device_get_softc(dev); + + if (sc->irq_cookie) + { + bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); + } + + if (sc->irq_res) + { + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); + } + + mtx_destroy(&sc->lock); + return (0); +} + +DRIVER_MODULE(iichid_acpi, acpi, iichid_acpi_driver, iichid_acpi_devclass, NULL, 0); +MODULE_DEPEND(iichid_acpi, acpi, 1, 1, 1); +MODULE_DEPEND(iichid_acpi, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); +MODULE_DEPEND(iichid_acpi, iichid, 1, 1, 1); +MODULE_VERSION(iichid_acpi, 1); Index: sys/dev/usb/hid.h =================================================================== --- /dev/null +++ sys/dev/usb/hid.h @@ -0,0 +1,240 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * Copyright (c) 1998 Lennart Augustsson. All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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 _HID_H_ +#define _HID_H_ + +#define UR_GET_HID_DESCRIPTOR 0x06 +#define UDESC_HID 0x21 +#define UDESC_REPORT 0x22 +#define UDESC_PHYSICAL 0x23 +#define UR_SET_HID_DESCRIPTOR 0x07 +#define UR_GET_REPORT 0x01 +#define UR_SET_REPORT 0x09 +#define UR_GET_IDLE 0x02 +#define UR_SET_IDLE 0x0a +#define UR_GET_PROTOCOL 0x03 +#define UR_SET_PROTOCOL 0x0b + +struct hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bcdHID[2]; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + struct { + uint8_t bDescriptorType; + uint8_t wDescriptorLength[2]; + } descrs[1]; +} __packed; + +#define HID_DESCRIPTOR_SIZE(n) (9+((n)*3)) + +/* 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_LEDS 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_SYSTEM 0x0085 +#define HUP_BARCODE_SCANNER 0x008b +#define HUP_SCALE 0x008c +#define HUP_CAMERA_CONTROL 0x0090 +#define HUP_ARCADE 0x0091 +#define HUP_MICROSOFT 0xff00 + +/* Usages, generic desktop */ +#define HUG_POINTER 0x0001 +#define HUG_MOUSE 0x0002 +#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 /* M$ Wireless Intellimouse Wheel */ +#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 +#define HUG_APPLE_EJECT 0x00b8 + +/* Usages Digitizers */ +#define HUD_UNDEFINED 0x0000 +#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, Consumer */ +#define HUC_AC_PAN 0x0238 + +#define HID_USAGE2(p,u) (((p) << 16) | (u)) + +#define UHID_INPUT_REPORT 0x01 +#define UHID_OUTPUT_REPORT 0x02 +#define UHID_FEATURE_REPORT 0x03 + +/* 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 + +#ifdef _KERNEL + +enum hid_kind { + hid_input, hid_output, hid_feature, hid_collection, hid_endcollection +}; + +struct hid_location { + uint32_t size; + uint32_t count; + uint32_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; + uint32_t flags; + /* Location */ + struct hid_location loc; +}; + +/* prototypes from "hid.c" */ + +struct hid_data *hid_start_parse(const void *d, size_t len, int kindset); +void hid_end_parse(struct hid_data *s); +int hid_get_item(struct hid_data *s, struct hid_item *h); +int hid_report_size(const void *buf, size_t len, enum hid_kind k, + uint8_t *id); +int hid_locate(const void *desc, size_t size, int32_t usage, + enum hid_kind kind, uint8_t index, struct hid_location *loc, + uint32_t *flags, uint8_t *id); +int32_t hid_get_data(const uint8_t *buf, size_t len, + struct hid_location *loc); +uint32_t hid_get_data_unsigned(const uint8_t *buf, size_t len, + struct hid_location *loc); +void hid_put_data_unsigned(uint8_t *buf, size_t len, + struct hid_location *loc, unsigned int value); +int hid_is_collection(const void *desc, size_t size, int32_t usage); +int32_t hid_item_resolution(struct hid_item *hi); +int hid_is_mouse(const void *d_ptr, uint16_t d_len); +int hid_is_keyboard(const void *d_ptr, uint16_t d_len); +#endif /* _KERNEL */ +#endif /* _HID_H_ */ Index: sys/dev/usb/hid.c =================================================================== --- /dev/null +++ sys/dev/usb/hid.c @@ -0,0 +1,916 @@ +/* $FreeBSD$ */ +/* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define HID_DEBUG_VAR hid_debug + +#ifdef HID_DEBUG_VAR +#ifdef HID_DEBUG + +#define DPRINTFN(n,fmt,...) do { \ + if ((HID_DEBUG_VAR) >= (n)) { \ + printf("%s: " fmt, \ + __FUNCTION__ ,##__VA_ARGS__); \ + } \ +} while (0) +#define DPRINTF(...) DPRINTFN(1, __VA_ARGS__) +#else +#define DPRINTF(...) do { } while (0) +#define DPRINTFN(...) do { } while (0) +#endif +#endif + +static void hid_clear_local(struct hid_item *); +static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); + +#define MAXUSAGE 64 +#define MAXPUSH 4 +#define MAXID 16 +#define MAXLOCCNT 1024 + +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 */ + uint8_t ncount; /* end usage item count */ + uint8_t icount; /* current usage item count */ + uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ + uint8_t pushlevel; /* current pushlevel */ + 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 */ +}; + +/*------------------------------------------------------------------------* + * hid_clear_local + *------------------------------------------------------------------------*/ +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 next_rID) +{ + uint8_t i; + + /* check for same report ID - optimise */ + + if (c->report_ID == next_rID) + 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 = next_rID; + + /* lookup last position for next rID */ + + if (next_rID == 0) { + i = 0; + } else { + for (i = 1; i != MAXID; i++) { + if (s->last_pos[i].rid == next_rID) + break; + if (s->last_pos[i].rid == 0) + break; + } + } + if (i != MAXID) { + s->last_pos[i].rid = next_rID; + c->loc.pos = s->last_pos[i].pos; + } else { + DPRINTF("Out of RID entries, position is set to zero!\n"); + c->loc.pos = 0; + } +} + +/*------------------------------------------------------------------------* + * hid_start_parse + *------------------------------------------------------------------------*/ +struct hid_data * +hid_start_parse(const void *d, size_t len, int kindset) +{ + struct hid_data *s; + + if ((kindset-1) & kindset) { + DPRINTFN(0, "Only one bit can be " + "set in the kindset\n"); + return (NULL); + } + + s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); + s->start = s->p = d; + s->end = ((const uint8_t *)d) + len; + s->kindset = kindset; + return (s); +} + +/*------------------------------------------------------------------------* + * hid_end_parse + *------------------------------------------------------------------------*/ +void +hid_end_parse(struct hid_data *s) +{ + if (s == NULL) + return; + + free(s, M_TEMP); +} + +/*------------------------------------------------------------------------* + * get byte from HID descriptor + *------------------------------------------------------------------------*/ +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); +} + +/*------------------------------------------------------------------------* + * hid_get_item + *------------------------------------------------------------------------*/ +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 */ + 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 { + DPRINTFN(1, "Using last usage\n"); + dval = s->usage_last; + } + s->icount ++; + /* + * Only copy HID item, increment position and return + * if correct kindset! + */ + if (s->kindset & (1 << c->kind)) { + *h = *c; + DPRINTFN(1, "%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 = (int8_t)hid_get_byte(s, 1); + mask = 0xFF; + break; + case 2: + dval = hid_get_byte(s, 1); + dval |= hid_get_byte(s, 1) << 8; + dval = (int16_t)dval; + 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); + DPRINTFN(0, "bad length %u (data=0x%02x)\n", + bSize, dval); + continue; + } + + switch (bType) { + case 0: /* Main */ + switch (bTag) { + case 8: /* Input */ + c->kind = hid_input; + 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 > MAXLOCCNT) { + DPRINTFN(0, "Number of " + "items(%u) truncated to %u\n", + (unsigned)(c->loc.count), + MAXLOCCNT); + s->ncount = MAXLOCCNT; + } 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; + 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; + goto ret; + case 12: /* End collection */ + c->kind = hid_endcollection; + if (c->collevel == 0) { + DPRINTFN(0, "invalid end collection\n"); + return (0); + } + c->collevel--; + *h = *c; + return (1); + default: + DPRINTFN(0, "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 { + DPRINTFN(0, "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 { + DPRINTFN(0, "Cannot pop " + "item @ %d\n", s->pushlevel); + } + break; + default: + DPRINTFN(0, "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 { + DPRINTFN(0, "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 { + DPRINTFN(0, "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: + DPRINTFN(0, "Local bTag=%d\n", bTag); + break; + } + break; + default: + DPRINTFN(0, "default bType=%d\n", bType); + break; + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * hid_report_size + *------------------------------------------------------------------------*/ +int +hid_report_size(const void *buf, size_t len, enum hid_kind k, uint8_t *id) +{ + struct hid_data *d; + struct hid_item h; + uint32_t temp; + uint32_t hpos; + uint32_t lpos; + uint8_t any_id; + + any_id = 0; + hpos = 0; + lpos = 0xFFFFFFFF; + + for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { + if (h.kind == k) { + /* check for ID-byte presence */ + if ((h.report_ID != 0) && !any_id) { + if (id != NULL) + *id = h.report_ID; + any_id = 1; + } + /* compute minimum */ + if (lpos > h.loc.pos) + lpos = h.loc.pos; + /* compute end position */ + temp = h.loc.pos + (h.loc.size * h.loc.count); + /* compute maximum */ + if (hpos < temp) + hpos = temp; + } + } + hid_end_parse(d); + + /* safety check - can happen in case of currupt descriptors */ + if (lpos > hpos) + temp = 0; + else + temp = hpos - lpos; + + /* check for ID byte */ + if (any_id) + temp += 8; + else if (id != NULL) + *id = 0; + + /* return length in bytes rounded up */ + return ((temp + 7) / 8); +} + +/*------------------------------------------------------------------------* + * hid_locate + *------------------------------------------------------------------------*/ +int +hid_locate(const void *desc, size_t size, int32_t u, enum hid_kind k, + uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) +{ + struct hid_data *d; + struct hid_item h; + + for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { + if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { + if (index--) + continue; + if (loc != NULL) + *loc = h.loc; + if (flags != NULL) + *flags = h.flags; + if (id != NULL) + *id = h.report_ID; + hid_end_parse(d); + return (1); + } + } + if (loc != NULL) + loc->size = 0; + if (flags != NULL) + *flags = 0; + if (id != NULL) + *id = 0; + hid_end_parse(d); + return (0); +} + +/*------------------------------------------------------------------------* + * hid_get_data + *------------------------------------------------------------------------*/ +static uint32_t +hid_get_data_sub(const uint8_t *buf, size_t len, struct hid_location *loc, + int is_signed) +{ + uint32_t hpos = loc->pos; + uint32_t hsize = loc->size; + uint32_t data; + uint32_t rpos; + uint8_t n; + + DPRINTFN(11, "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; + + /* Mask and sign extend in one */ + if (is_signed != 0) + data = (int32_t)((int32_t)data << n) >> n; + else + data = (uint32_t)((uint32_t)data << n) >> n; + + DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", + loc->pos, loc->size, (long)data); + return (data); +} + +int32_t +hid_get_data(const uint8_t *buf, size_t len, struct hid_location *loc) +{ + return (hid_get_data_sub(buf, len, loc, 1)); +} + +uint32_t +hid_get_data_unsigned(const uint8_t *buf, size_t len, struct hid_location *loc) +{ + return (hid_get_data_sub(buf, len, loc, 0)); +} + +/*------------------------------------------------------------------------* + * hid_put_data + *------------------------------------------------------------------------*/ +void +hid_put_data_unsigned(uint8_t *buf, size_t len, + struct hid_location *loc, unsigned int value) +{ + uint32_t hpos = loc->pos; + uint32_t hsize = loc->size; + uint64_t data; + uint64_t mask; + uint32_t rpos; + uint8_t n; + + DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); + + /* Range check and limit */ + if (hsize == 0) + return; + if (hsize > 32) + hsize = 32; + + /* Put data in a safe way */ + rpos = (hpos / 8); + n = (hsize + 7) / 8; + data = ((uint64_t)value) << (hpos % 8); + mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); + rpos += n; + while (n--) { + rpos--; + if (rpos < len) { + buf[rpos] &= ~(mask >> (8 * n)); + buf[rpos] |= (data >> (8 * n)); + } + } +} + +/*------------------------------------------------------------------------* + * hid_is_collection + *------------------------------------------------------------------------*/ +int +hid_is_collection(const void *desc, size_t size, int32_t usage) +{ + struct hid_data *hd; + struct hid_item hi; + int err; + + hd = hid_start_parse(desc, size, hid_input); + if (hd == NULL) + return (0); + + while ((err = hid_get_item(hd, &hi))) { + if (hi.kind == hid_collection && + hi.usage == usage) + break; + } + hid_end_parse(hd); + return (err); +} + +/*------------------------------------------------------------------------* + * calculate HID item resolution. unit/mm for distances, unit/rad for angles + *------------------------------------------------------------------------*/ +int32_t +hid_item_resolution(struct hid_item *hi) +{ + /* + * hid unit scaling table according to HID Usage Table Review + * Request 39 Tbl 17 http://www.usb.org/developers/hidpage/HUTRR39b.pdf + */ + static const int64_t scale[0x10][2] = { + [0x00] = { 1, 1 }, + [0x01] = { 1, 10 }, + [0x02] = { 1, 100 }, + [0x03] = { 1, 1000 }, + [0x04] = { 1, 10000 }, + [0x05] = { 1, 100000 }, + [0x06] = { 1, 1000000 }, + [0x07] = { 1, 10000000 }, + [0x08] = { 100000000, 1 }, + [0x09] = { 10000000, 1 }, + [0x0A] = { 1000000, 1 }, + [0x0B] = { 100000, 1 }, + [0x0C] = { 10000, 1 }, + [0x0D] = { 1000, 1 }, + [0x0E] = { 100, 1 }, + [0x0F] = { 10, 1 }, + }; + int64_t logical_size; + int64_t physical_size; + int64_t multiplier; + int64_t divisor; + int64_t resolution; + + switch (hi->unit) { + case HUM_CENTIMETER: + multiplier = 1; + divisor = 10; + break; + case HUM_INCH: + multiplier = 10; + divisor = 254; + break; + case HUM_RADIAN: + multiplier = 1; + divisor = 1; + break; + case HUM_DEGREE: + multiplier = 573; + divisor = 10; + break; + default: + return (0); + } + + if ((hi->logical_maximum <= hi->logical_minimum) || + (hi->physical_maximum <= hi->physical_minimum) || + (hi->unit_exponent < 0) || (hi->unit_exponent >= nitems(scale))) + return (0); + + logical_size = (int64_t)hi->logical_maximum - + (int64_t)hi->logical_minimum; + physical_size = (int64_t)hi->physical_maximum - + (int64_t)hi->physical_minimum; + /* Round to ceiling */ + resolution = logical_size * multiplier * scale[hi->unit_exponent][0] / + (physical_size * divisor * scale[hi->unit_exponent][1]); + + if (resolution > INT32_MAX) + return (0); + + return (resolution); +} + +/*------------------------------------------------------------------------* + * hid_is_mouse + * + * This function will decide if a USB descriptor belongs to a USB mouse. + * + * Return values: + * Zero: Not a USB mouse. + * Else: Is a USB mouse. + *------------------------------------------------------------------------*/ +int +hid_is_mouse(const void *d_ptr, uint16_t d_len) +{ + struct hid_data *hd; + struct hid_item hi; + int mdepth; + int found; + + hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); + if (hd == NULL) + return (0); + + mdepth = 0; + found = 0; + + while (hid_get_item(hd, &hi)) { + switch (hi.kind) { + case hid_collection: + if (mdepth != 0) + mdepth++; + else if (hi.collection == 1 && + hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) + mdepth++; + break; + case hid_endcollection: + if (mdepth != 0) + mdepth--; + break; + case hid_input: + if (mdepth == 0) + break; + if (hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && + (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) + found++; + if (hi.usage == + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && + (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) + found++; + break; + default: + break; + } + } + hid_end_parse(hd); + return (found); +} + +/*------------------------------------------------------------------------* + * hid_is_keyboard + * + * This function will decide if a USB descriptor belongs to a USB keyboard. + * + * Return values: + * Zero: Not a USB keyboard. + * Else: Is a USB keyboard. + *------------------------------------------------------------------------*/ +int +hid_is_keyboard(const void *d_ptr, uint16_t d_len) +{ + if (hid_is_collection(d_ptr, d_len, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) + return (1); + return (0); +} Index: sys/dev/usb/usb_hid.c =================================================================== --- sys/dev/usb/usb_hid.c +++ sys/dev/usb/usb_hid.c @@ -68,703 +68,6 @@ #include #endif /* USB_GLOBAL_INCLUDE_FILE */ -static void hid_clear_local(struct hid_item *); -static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); - -#define MAXUSAGE 64 -#define MAXPUSH 4 -#define MAXID 16 -#define MAXLOCCNT 1024 - -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 */ - uint32_t ncount; /* end usage item count */ - uint32_t icount; /* current usage item count */ - uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ - uint8_t pushlevel; /* current pushlevel */ - 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 */ -}; - -/*------------------------------------------------------------------------* - * hid_clear_local - *------------------------------------------------------------------------*/ -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 next_rID) -{ - uint8_t i; - - /* check for same report ID - optimise */ - - if (c->report_ID == next_rID) - 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 = next_rID; - - /* lookup last position for next rID */ - - if (next_rID == 0) { - i = 0; - } else { - for (i = 1; i != MAXID; i++) { - if (s->last_pos[i].rid == next_rID) - break; - if (s->last_pos[i].rid == 0) - break; - } - } - if (i != MAXID) { - s->last_pos[i].rid = next_rID; - c->loc.pos = s->last_pos[i].pos; - } else { - DPRINTF("Out of RID entries, position is set to zero!\n"); - c->loc.pos = 0; - } -} - -/*------------------------------------------------------------------------* - * hid_start_parse - *------------------------------------------------------------------------*/ -struct hid_data * -hid_start_parse(const void *d, usb_size_t len, int kindset) -{ - struct hid_data *s; - - if ((kindset-1) & kindset) { - DPRINTFN(0, "Only one bit can be " - "set in the kindset\n"); - return (NULL); - } - - s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); - s->start = s->p = d; - s->end = ((const uint8_t *)d) + len; - s->kindset = kindset; - return (s); -} - -/*------------------------------------------------------------------------* - * hid_end_parse - *------------------------------------------------------------------------*/ -void -hid_end_parse(struct hid_data *s) -{ - if (s == NULL) - return; - - free(s, M_TEMP); -} - -/*------------------------------------------------------------------------* - * get byte from HID descriptor - *------------------------------------------------------------------------*/ -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); -} - -/*------------------------------------------------------------------------* - * hid_get_item - *------------------------------------------------------------------------*/ -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 */ - 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 { - DPRINTFN(1, "Using last usage\n"); - dval = s->usage_last; - } - s->icount ++; - /* - * Only copy HID item, increment position and return - * if correct kindset! - */ - if (s->kindset & (1 << c->kind)) { - *h = *c; - DPRINTFN(1, "%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 = (int8_t)hid_get_byte(s, 1); - mask = 0xFF; - break; - case 2: - dval = hid_get_byte(s, 1); - dval |= hid_get_byte(s, 1) << 8; - dval = (int16_t)dval; - 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); - DPRINTFN(0, "bad length %u (data=0x%02x)\n", - bSize, dval); - continue; - } - - switch (bType) { - case 0: /* Main */ - switch (bTag) { - case 8: /* Input */ - c->kind = hid_input; - ret: - c->flags = dval; - 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 > MAXLOCCNT) { - DPRINTFN(0, "Number of " - "items(%u) truncated to %u\n", - (unsigned)(c->loc.count), - MAXLOCCNT); - s->ncount = MAXLOCCNT; - } 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; - 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; - goto ret; - case 12: /* End collection */ - c->kind = hid_endcollection; - if (c->collevel == 0) { - DPRINTFN(0, "invalid end collection\n"); - return (0); - } - c->collevel--; - *h = *c; - return (1); - default: - DPRINTFN(0, "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 { - DPRINTFN(0, "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 { - DPRINTFN(0, "Cannot pop " - "item @ %d\n", s->pushlevel); - } - break; - default: - DPRINTFN(0, "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 { - DPRINTFN(0, "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 { - DPRINTFN(0, "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: - DPRINTFN(0, "Local bTag=%d\n", bTag); - break; - } - break; - default: - DPRINTFN(0, "default bType=%d\n", bType); - break; - } - } - return (0); -} - -/*------------------------------------------------------------------------* - * hid_report_size - *------------------------------------------------------------------------*/ -int -hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) -{ - struct hid_data *d; - struct hid_item h; - uint32_t temp; - uint32_t hpos; - uint32_t lpos; - uint8_t any_id; - - any_id = 0; - hpos = 0; - lpos = 0xFFFFFFFF; - - for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { - if (h.kind == k) { - /* check for ID-byte presence */ - if ((h.report_ID != 0) && !any_id) { - if (id != NULL) - *id = h.report_ID; - any_id = 1; - } - /* compute minimum */ - if (lpos > h.loc.pos) - lpos = h.loc.pos; - /* compute end position */ - temp = h.loc.pos + (h.loc.size * h.loc.count); - /* compute maximum */ - if (hpos < temp) - hpos = temp; - } - } - hid_end_parse(d); - - /* safety check - can happen in case of currupt descriptors */ - if (lpos > hpos) - temp = 0; - else - temp = hpos - lpos; - - /* check for ID byte */ - if (any_id) - temp += 8; - else if (id != NULL) - *id = 0; - - /* return length in bytes rounded up */ - return ((temp + 7) / 8); -} - -/*------------------------------------------------------------------------* - * hid_locate - *------------------------------------------------------------------------*/ -int -hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k, - uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) -{ - struct hid_data *d; - struct hid_item h; - - for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { - if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { - if (index--) - continue; - if (loc != NULL) - *loc = h.loc; - if (flags != NULL) - *flags = h.flags; - if (id != NULL) - *id = h.report_ID; - hid_end_parse(d); - return (1); - } - } - if (loc != NULL) - loc->size = 0; - if (flags != NULL) - *flags = 0; - if (id != NULL) - *id = 0; - hid_end_parse(d); - return (0); -} - -/*------------------------------------------------------------------------* - * hid_get_data - *------------------------------------------------------------------------*/ -static uint32_t -hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc, - int is_signed) -{ - uint32_t hpos = loc->pos; - uint32_t hsize = loc->size; - uint32_t data; - uint32_t rpos; - uint8_t n; - - DPRINTFN(11, "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; - - /* Mask and sign extend in one */ - if (is_signed != 0) - data = (int32_t)((int32_t)data << n) >> n; - else - data = (uint32_t)((uint32_t)data << n) >> n; - - DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", - loc->pos, loc->size, (long)data); - return (data); -} - -int32_t -hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) -{ - return (hid_get_data_sub(buf, len, loc, 1)); -} - -uint32_t -hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc) -{ - return (hid_get_data_sub(buf, len, loc, 0)); -} - -/*------------------------------------------------------------------------* - * hid_put_data - *------------------------------------------------------------------------*/ -void -hid_put_data_unsigned(uint8_t *buf, usb_size_t len, - struct hid_location *loc, unsigned int value) -{ - uint32_t hpos = loc->pos; - uint32_t hsize = loc->size; - uint64_t data; - uint64_t mask; - uint32_t rpos; - uint8_t n; - - DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); - - /* Range check and limit */ - if (hsize == 0) - return; - if (hsize > 32) - hsize = 32; - - /* Put data in a safe way */ - rpos = (hpos / 8); - n = (hsize + 7) / 8; - data = ((uint64_t)value) << (hpos % 8); - mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); - rpos += n; - while (n--) { - rpos--; - if (rpos < len) { - buf[rpos] &= ~(mask >> (8 * n)); - buf[rpos] |= (data >> (8 * n)); - } - } -} - -/*------------------------------------------------------------------------* - * hid_is_collection - *------------------------------------------------------------------------*/ -int -hid_is_collection(const void *desc, usb_size_t size, int32_t usage) -{ - struct hid_data *hd; - struct hid_item hi; - int err; - - hd = hid_start_parse(desc, size, hid_input); - if (hd == NULL) - return (0); - - while ((err = hid_get_item(hd, &hi))) { - if (hi.kind == hid_collection && - hi.usage == usage) - break; - } - hid_end_parse(hd); - return (err); -} - /*------------------------------------------------------------------------* * hid_get_descriptor_from_usb * @@ -775,7 +78,7 @@ * NULL: No more HID descriptors. * Else: Pointer to HID descriptor. *------------------------------------------------------------------------*/ -struct usb_hid_descriptor * +struct hid_descriptor * hid_get_descriptor_from_usb(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id) { @@ -786,7 +89,7 @@ } while ((desc = usb_desc_foreach(cd, desc))) { if ((desc->bDescriptorType == UDESC_HID) && - (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { + (desc->bLength >= HID_DESCRIPTOR_SIZE(0))) { return (void *)desc; } if (desc->bDescriptorType == UDESC_INTERFACE) { @@ -812,7 +115,7 @@ struct malloc_type *mem, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); - struct usb_hid_descriptor *hid; + struct hid_descriptor *hid; usb_error_t err; if ((iface == NULL) || (iface->idesc == NULL)) { @@ -850,152 +153,3 @@ return (USB_ERR_NORMAL_COMPLETION); } -/*------------------------------------------------------------------------* - * calculate HID item resolution. unit/mm for distances, unit/rad for angles - *------------------------------------------------------------------------*/ -int32_t -hid_item_resolution(struct hid_item *hi) -{ - /* - * hid unit scaling table according to HID Usage Table Review - * Request 39 Tbl 17 http://www.usb.org/developers/hidpage/HUTRR39b.pdf - */ - static const int64_t scale[0x10][2] = { - [0x00] = { 1, 1 }, - [0x01] = { 1, 10 }, - [0x02] = { 1, 100 }, - [0x03] = { 1, 1000 }, - [0x04] = { 1, 10000 }, - [0x05] = { 1, 100000 }, - [0x06] = { 1, 1000000 }, - [0x07] = { 1, 10000000 }, - [0x08] = { 100000000, 1 }, - [0x09] = { 10000000, 1 }, - [0x0A] = { 1000000, 1 }, - [0x0B] = { 100000, 1 }, - [0x0C] = { 10000, 1 }, - [0x0D] = { 1000, 1 }, - [0x0E] = { 100, 1 }, - [0x0F] = { 10, 1 }, - }; - int64_t logical_size; - int64_t physical_size; - int64_t multiplier; - int64_t divisor; - int64_t resolution; - - switch (hi->unit) { - case HUM_CENTIMETER: - multiplier = 1; - divisor = 10; - break; - case HUM_INCH: - multiplier = 10; - divisor = 254; - break; - case HUM_RADIAN: - multiplier = 1; - divisor = 1; - break; - case HUM_DEGREE: - multiplier = 573; - divisor = 10; - break; - default: - return (0); - } - - if ((hi->logical_maximum <= hi->logical_minimum) || - (hi->physical_maximum <= hi->physical_minimum) || - (hi->unit_exponent < 0) || (hi->unit_exponent >= nitems(scale))) - return (0); - - logical_size = (int64_t)hi->logical_maximum - - (int64_t)hi->logical_minimum; - physical_size = (int64_t)hi->physical_maximum - - (int64_t)hi->physical_minimum; - /* Round to ceiling */ - resolution = logical_size * multiplier * scale[hi->unit_exponent][0] / - (physical_size * divisor * scale[hi->unit_exponent][1]); - - if (resolution > INT32_MAX) - return (0); - - return (resolution); -} - -/*------------------------------------------------------------------------* - * hid_is_mouse - * - * This function will decide if a USB descriptor belongs to a USB mouse. - * - * Return values: - * Zero: Not a USB mouse. - * Else: Is a USB mouse. - *------------------------------------------------------------------------*/ -int -hid_is_mouse(const void *d_ptr, uint16_t d_len) -{ - struct hid_data *hd; - struct hid_item hi; - int mdepth; - int found; - - hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); - if (hd == NULL) - return (0); - - mdepth = 0; - found = 0; - - while (hid_get_item(hd, &hi)) { - switch (hi.kind) { - case hid_collection: - if (mdepth != 0) - mdepth++; - else if (hi.collection == 1 && - hi.usage == - HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) - mdepth++; - break; - case hid_endcollection: - if (mdepth != 0) - mdepth--; - break; - case hid_input: - if (mdepth == 0) - break; - if (hi.usage == - HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && - (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) - found++; - if (hi.usage == - HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && - (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) - found++; - break; - default: - break; - } - } - hid_end_parse(hd); - return (found); -} - -/*------------------------------------------------------------------------* - * hid_is_keyboard - * - * This function will decide if a USB descriptor belongs to a USB keyboard. - * - * Return values: - * Zero: Not a USB keyboard. - * Else: Is a USB keyboard. - *------------------------------------------------------------------------*/ -int -hid_is_keyboard(const void *d_ptr, uint16_t d_len) -{ - if (hid_is_collection(d_ptr, d_len, - HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) - return (1); - return (0); -} Index: sys/dev/usb/usbhid.h =================================================================== --- sys/dev/usb/usbhid.h +++ sys/dev/usb/usbhid.h @@ -35,241 +35,18 @@ #include #endif -#define UR_GET_HID_DESCRIPTOR 0x06 -#define UDESC_HID 0x21 -#define UDESC_REPORT 0x22 -#define UDESC_PHYSICAL 0x23 -#define UR_SET_HID_DESCRIPTOR 0x07 -#define UR_GET_REPORT 0x01 -#define UR_SET_REPORT 0x09 -#define UR_GET_IDLE 0x02 -#define UR_SET_IDLE 0x0a -#define UR_GET_PROTOCOL 0x03 -#define UR_SET_PROTOCOL 0x0b - -struct usb_hid_descriptor { - uByte bLength; - uByte bDescriptorType; - uWord bcdHID; - uByte bCountryCode; - uByte bNumDescriptors; - struct { - uByte bDescriptorType; - uWord wDescriptorLength; - } descrs[1]; -} __packed; - -#define USB_HID_DESCRIPTOR_SIZE(n) (9+((n)*3)) - -/* 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_LEDS 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_SYSTEM 0x0085 -#define HUP_BARCODE_SCANNER 0x008b -#define HUP_SCALE 0x008c -#define HUP_CAMERA_CONTROL 0x0090 -#define HUP_ARCADE 0x0091 -#define HUP_MICROSOFT 0xff00 - -/* Usages, generic desktop */ -#define HUG_POINTER 0x0001 -#define HUG_MOUSE 0x0002 -#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 /* M$ Wireless Intellimouse Wheel */ -#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 -#define HUG_APPLE_EJECT 0x00b8 - -/* Usages Digitizers */ -#define HUD_UNDEFINED 0x0000 -#define HUD_DIGITIZER 0x0001 -#define HUD_PEN 0x0002 -#define HUD_TOUCHSCREEN 0x0004 -#define HUD_TOUCHPAD 0x0005 -#define HUD_CONFIG 0x000e -#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 -#define HUD_CONFIDENCE 0x0047 -#define HUD_WIDTH 0x0048 -#define HUD_HEIGHT 0x0049 -#define HUD_CONTACTID 0x0051 -#define HUD_INPUT_MODE 0x0052 -#define HUD_DEVICE_INDEX 0x0053 -#define HUD_CONTACTCOUNT 0x0054 -#define HUD_CONTACT_MAX 0x0055 -#define HUD_SCAN_TIME 0x0056 -#define HUD_BUTTON_TYPE 0x0059 - -/* Usages, Consumer */ -#define HUC_AC_PAN 0x0238 - -#define HID_USAGE2(p,u) (((p) << 16) | (u)) - -#define UHID_INPUT_REPORT 0x01 -#define UHID_OUTPUT_REPORT 0x02 -#define UHID_FEATURE_REPORT 0x03 - -/* 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 - -/* Units of Measure */ -#define HUM_CENTIMETER 0x11 -#define HUM_RADIAN 0x12 -#define HUM_INCH 0x13 -#define HUM_DEGREE 0x14 +#include #ifdef _KERNEL struct usb_config_descriptor; -enum hid_kind { - hid_input, hid_output, hid_feature, hid_collection, hid_endcollection -}; - -struct hid_location { - uint32_t size; - uint32_t count; - uint32_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; - uint32_t flags; - /* Location */ - struct hid_location loc; -}; - /* prototypes from "usb_hid.c" */ -struct hid_data *hid_start_parse(const void *d, usb_size_t len, int kindset); -void hid_end_parse(struct hid_data *s); -int hid_get_item(struct hid_data *s, struct hid_item *h); -int hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, - uint8_t *id); -int hid_locate(const void *desc, usb_size_t size, int32_t usage, - enum hid_kind kind, uint8_t index, struct hid_location *loc, - uint32_t *flags, uint8_t *id); -int32_t hid_get_data(const uint8_t *buf, usb_size_t len, - struct hid_location *loc); -uint32_t hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, - struct hid_location *loc); -void hid_put_data_unsigned(uint8_t *buf, usb_size_t len, - struct hid_location *loc, unsigned int value); -int hid_is_collection(const void *desc, usb_size_t size, int32_t usage); -struct usb_hid_descriptor *hid_get_descriptor_from_usb( +struct hid_descriptor *hid_get_descriptor_from_usb( struct usb_config_descriptor *cd, struct usb_interface_descriptor *id); usb_error_t usbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, void **descp, uint16_t *sizep, struct malloc_type *mem, uint8_t iface_index); -int32_t hid_item_resolution(struct hid_item *hi); -int hid_is_mouse(const void *d_ptr, uint16_t d_len); -int hid_is_keyboard(const void *d_ptr, uint16_t d_len); #endif /* _KERNEL */ #endif /* _USB_HID_H_ */ Index: sys/modules/acpi/acpi_iichid/Makefile =================================================================== --- /dev/null +++ sys/modules/acpi/acpi_iichid/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/input +KMOD = acpi_iichid +SRCS = iichid_acpi.c device_if.h bus_if.h iicbus_if.h vnode_if.h opt_acpi.h acpi_if.h + +.include Index: sys/modules/i2c/Makefile =================================================================== --- sys/modules/i2c/Makefile +++ sys/modules/i2c/Makefile @@ -12,6 +12,7 @@ iic \ iicbb \ iicbus \ + iichid \ iicsmb \ isl \ isl12xx \ Index: sys/modules/i2c/iichid/Makefile =================================================================== --- /dev/null +++ sys/modules/i2c/iichid/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/input +KMOD = iichid +SRCS = iichid.c device_if.h bus_if.h iicbus_if.h vnode_if.h + +.include Index: sys/modules/usb/ukbd/Makefile =================================================================== --- sys/modules/usb/ukbd/Makefile +++ sys/modules/usb/ukbd/Makefile @@ -32,6 +32,6 @@ KMOD= ukbd SRCS= opt_bus.h opt_evdev.h opt_kbd.h opt_ukbd.h opt_usb.h \ device_if.h bus_if.h usb_if.h usbdevs.h \ - ukbd.c + ../hid.c ukbd.c .include