--- hid.c.orig 2008-02-28 16:01:37.000000000 -0800 +++ hid.c 2008-02-28 14:39:40.000000000 -0800 @@ -1,5 +1,6 @@ /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ +/* UMSPATCH-dhh-1a 02/24/08 */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/hid.c,v 1.25.2.1 2006/03/25 04:46:52 iedowse Exp $"); @@ -366,6 +367,105 @@ } } +/* + * Discover and return the size of the input data report with id + * 'report_id'. If the report descriptor provides only a single + * report with no report id, ask for report id 0. Return the size + * in bytes of the corresponding input data report, including the + * id byte which will be prepended by the device. + + * This replaces hid_report_size(), which works only for devices + * which provide a single input data report, and returns the wrong + * ID and wrong size when multiple reports are present (except in the + * happy case in which the first numbered ID is the required ID. + * + * Unfortunately, hid_report_size() is used in uhid.c and ucycom.c. + * It should not be changed until this function can be shown to be + * a proper replacement for those uses. + */ + +#ifdef USB_DEBUG +#define HPRINTF(x) if (usbdebug) logprintf x +#else +#define HPRINTF(x) +#endif + +int +hid_reportID_size(void *buf, int len, enum hid_kind k, u_int8_t report_id) +{ + struct hid_data *d; + struct hid_item h; + int hi, lo, size; + + hi = lo = -1; + for (d = hid_start_parse(buf, len, 1< %d bits\n",size)); + } + HPRINTF(("hid_reportID_size: %d bytes\n",(size +7) / 8)); + return ((size + 7) / 8); +} + +/* + * Like hid_locate(), but grab the entire item structure. + */ + +int +hid_hlocate(void *desc, int size, u_int32_t u, enum hid_kind k,struct hid_item *h) +{ + struct hid_data *d; + + for (d = hid_start_parse(desc, size, 1<kind == k && !(h->flags & HIO_CONST) && h->usage == u) { + hid_end_parse(d); + return (1); + } + } + hid_end_parse(d); + h->loc.size = 0; + return (0); +} + +/* The original, incorrect hid_report_size(). + */ + int hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *idp) { --- ums.c.orig 2008-02-28 16:01:37.000000000 -0800 +++ ums.c 2008-02-28 14:39:40.000000000 -0800 @@ -11,29 +11,30 @@ * 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 + * 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. - * 3. All advertising materials mentioning features or use of this software + * 3. all advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its + * this product includes software developed by the netbsd + * foundation, inc. and its contributors. + * 4. neither the name of the netbsd foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * - * 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. + * 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. */ +/* UMSPATCH-dhh-1a 02/24/08 */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/ums.c,v 1.77.2.5 2007/06/17 09:38:26 brueffer Exp $"); @@ -63,16 +64,28 @@ #include #include +#include +#include #include "usbdevs.h" #include #include #include +/* Define this while the code is experimental + * set umsdebug to 1 + * use DPRINTF for enhanced reporting ###%%% + * + * [dhh 02/10/08] + */ +#ifndef USB_DEBUG +#define USB_DEBUG +#endif + #ifdef USB_DEBUG #define DPRINTF(x) if (umsdebug) logprintf x #define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x -int umsdebug = 0; +int umsdebug = 1; SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW, &umsdebug, 0, "ums debug level"); @@ -85,10 +98,11 @@ #define MS_TO_TICKS(ms) ((ms) * hz / 1000) -#define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */ +#define QUEUE_BUFSIZE 960 /* MUST be divisible by 5, 8 _and_ 12 */ struct ums_softc { device_t sc_dev; /* base device */ + usbd_device_handle sc_device; /* usb device */ usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; @@ -96,9 +110,12 @@ u_char *sc_ibuf; u_int8_t sc_iid; int sc_isize; - struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_t; + struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_t, sc_loc_w; struct hid_location *sc_loc_btn; + void *sc_repdesc; + int sc_repdesc_size; + usb_callout_t callout_handle; /* for spurious button ups */ int sc_enabled; @@ -106,8 +123,10 @@ int flags; /* device configuration */ #define UMS_Z 0x01 /* z direction available */ -#define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */ -#define UMS_T 0x04 /* aa direction available (tilt) */ +#define UMS_W 0x02 /* wheel data available */ +#define UMS_T 0x04 /* tilt wheel data available */ +#define UMS_REVZ 0x08 /* Z-axis is reversed */ +#define UMS_SPUR_BUT_UP 0x10 /* spurious button up events */ int nbuttons; #define MAX_BUTTONS 31 /* chosen because sc_buttons is int */ @@ -177,8 +196,12 @@ if (err) return (UMATCH_NONE); - if (hid_is_collection(desc, size, - HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) + /* + * If class is HID and protocol is MOUSE, attach. Subclass + * should not be relevant. [dhh] + */ + if ((id->bInterfaceClass == UICLASS_HID) && + (id->bInterfaceProtocol == UIPROTO_MOUSE)) ret = UMATCH_IFACECLASS; else ret = UMATCH_NONE; @@ -187,24 +210,108 @@ return (ret); } +/* + * The most basic mouse device supported by this driver is a + * mouse providing data for motion along the X and Y axes, with + * two or three buttons. Buttons are reported in one byte, + * and each motion axis is reported in one byte. Mouse up/down + * events are represented by 1 bit per button in the button byte). + + * When operating in this most basic mode mouse reports 3 bytes + * of input data for each event: + * [button, X, Y] + * + * Some USB mice make this basic protocol available to the bios for + * use before the OS is loaded. This is "boot protocol". + * + * XFree86 , and hence Xorg, has for some time supported mice with + * a wheel or roller, where the wheel data is presented as a third + * (Z) axis, and the wheel is also used as the middle button. This style + * of mouse is common for modern USB mice, and the X server mouse driver + * typically maps the Z axis to pseudo-buttons, and the display driver + * subsequently uses the data for vertical scrolling. + * + * Some wheel mice also generate data when the wheel axis is pushed + * sideways or "tilted". Xorg and XFree permit this data to be mapped for + * horizontal scrolling, but the current X drivers do not actually + * read the tilt data, and earlier versions of this driver read it but + * did not write it (the "sysmouse" protocol packet size is too small to + * handle more data). This driver writes tilt data if "mode level 3" is + * set via ioctl. An X driver (or modified version of moused) which + * handles the required extended protocol and sets protocol level 3 + * must be in place. If the extended X driver is not in place, this + * driver will operate at mode level 1 (as requested by the X driver) + * and the wheel will work but not the "tilt". For either version, + * the display driver (X) must be specifically configured (in the X + * config file) to accept Z and/or TILT data. (see ...../README.mouse + * somewhere in your X sources for a description of ZAxisMapping). + * + * Wheel mice, when properly initialized, will communicate in a + * "report protocol" which may carry more information than the basic + * boot protocol. If "report protocol" is set, the report descriptor + * may include one or more report descriptions. If only one report is + * available it will not have a report id, and a mouse report will deliver + * 4 data bytes per event: + * [button, X, Y, Z] + * + * If a tilt capability is present, a fifth byte may appear: + * [button, X, Y, Z, Tilt] + * + * Mice with multiple report IDs will have an extra "id" byte prepended, + * yielding + * [id, button, X, Y, Z] + * or + * [id, button, X, Y, Z, Tilt] + * + * This driver sets up wheel mice to deliver wheel data on the + * Z channel, and tilt data, if present, is delivered on the byte + * following the Z channel, as shown above. Again, tilt data will be + * written only if the extended SYSMOUSE protocol is defined, and the + * X mouse driver modified to accept it. Moused and the sysmouse/syscons + * driver must also be modified for the extended protocol, and the mode + * level set to level 3. The PS/2 driver might also want to get involved. + * + * In this driver, mice with a wheel are expected to report the + * presence of extra data in response to one of HUG_Z, HUG_WHEEL, + * or HUG_TWHEEL (tilt wheel) [see usbhid.h]. + * + * The mouse will be initialized to use report protocol, and to deliver + * reports only when changed data is available. The mouse is queried + * to discover the size of interrupt reports, and the report ID if + * multiple report IDs are in use. This is sufficient to configure + * "conforming" mice without quirks. Only mice which do not behave + * according to the HID specification should require "quirks". + * + * Yeah...right... + * + * [dhh 02/10/08] + */ + USB_ATTACH(ums) { USB_ATTACH_START(ums, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; + struct hid_item h; int size; void *desc; usbd_status err; char devinfo[1024]; - u_int32_t flags; + u_int32_t xflags,yflags,zflags,wflags,tflags; int i; - struct hid_location loc_btn; + int item_id = 0; + int no_report = 0; + int no_support = 0; + int id_offset = 0; + u_int8_t protocol = 0xff; + usbd_status ustatus; sc->sc_disconnected = 1; + sc->sc_device = uaa->device; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); - usbd_devinfo(uaa->device, USBD_SHOW_INTERFACE_CLASS, devinfo); + usbd_devinfo(uaa->device, USBD_SHOW_DEVICE_CLASS|USBD_SHOW_INTERFACE_CLASS, devinfo); USB_ATTACH_SETUP; ed = usbd_interface2endpoint_descriptor(iface, 0); if (!ed) { @@ -213,9 +320,9 @@ USB_ATTACH_ERROR_RETURN; } - DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d " + DPRINTFN(10,("%s: attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" - " bInterval=%d\n", + " bInterval=%d\n",USBDEVNAME(sc->sc_dev), ed->bLength, ed->bDescriptorType, UE_GET_ADDR(ed->bEndpointAddress), UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out", @@ -229,61 +336,250 @@ USB_ATTACH_ERROR_RETURN; } + /* + * This is for information only. + */ + if(usbd_get_protocol(sc->sc_iface, &protocol)) + { + DPRINTF(("%s: attach: can't get initial protocol\n", + USBDEVNAME(sc->sc_dev))); + } + else + { + DPRINTF(("%s: attach: initial protocol = %d\n", + USBDEVNAME(sc->sc_dev),protocol)); + } + + /* Initialize the mouse to report protocol and infinite idle rate. + * These should be defaults, and usually are... but not always. + * + * Some devices default to report protocol, some don't, so unilaterally + * attempt to set report protocol. This may fail, especially if the + * mouse does not implement SET_PROTOCOL, which is permissible if it + * does not implement a boot protocol. Failure of set_protocol is taken + * to imply that the protocol we want is the only one available, and + * we just shouldn't worry about it. + * + * If report protocol can't be set, the 3 byte boot protocol will be + * established below. The output protocol won't change (and will be + * selected by the application by setting mode level). Wheels don't + * work in this mode. This should happen only in the strikingly + * unusual case that set_protocol fails but get_protocol succeeds and + * claims the protocol is 0 (boot protocol). + * + * [dhh 02/10/08] + */ + if(usbd_set_protocol(sc->sc_iface, 1)) + { + /* + * If it fails, assume the only protocol available is + * report protocol, but check again below. + */ + protocol = 1; + DPRINTF(("%s: attach: could not set input protocol to REPORT\n", + USBDEVNAME(sc->sc_dev))); + } + else + DPRINTF(("%s: attach: set input protocol to REPORT (1)\n", + USBDEVNAME(sc->sc_dev))); + + /* + * This should verify that the protocol is "report" protocol (1) + * or just fail and leave it set that way. If it succeeds, but + * claims the protocol is set to 0, despite our best efforts, + * we can do naught but believe it. + */ + + if(usbd_get_protocol(sc->sc_iface, &protocol)) + { + DPRINTF(("%s: attach: couldn't get final input protocol", + USBDEVNAME(sc->sc_dev))); + DPRINTF(("...using %d (%s)\n",protocol,protocol ? "REPORT" : "BOOT")); + } + else + DPRINTF(("%s: attach: final input protocol = %d (%s)\n", + USBDEVNAME(sc->sc_dev),protocol,protocol ? "REPORT" : "BOOT")); + + /* + * The hid spec recommends that mice be set to report only upon data + * change. Some folks don't seem to agree. I have an MS Notebook Optical + * Mouse 3000 that reports continuously on ID 0x15, even though its + * position reports are on 0x11. This call attempts to force spec behavior. + * (It doesn't work for the 3000). + * [dhh 02/10/08] + */ + + if((ustatus = usbd_set_idle(sc->sc_iface,0x0,0))) + { + printf("%s: attach: usbd_set_idle(interface,0,0) FAILED, status %d\n", + USBDEVNAME(sc->sc_dev),ustatus); + } + else + printf("%s: attach: usbd_set_idle(interface,0,0) SUCCEEDED, status %d\n", + USBDEVNAME(sc->sc_dev),ustatus); + + /* + * Locate the mouse data items. X and Y axes are expected + * (or the mouse is useless, or not a mouse). + * W(heel) and T(ilt wheel) may or may not be present. Wheel data, + * if present, will be assigned to the Z axis. + * Up to 8 buttons may be reported by the device and recorded as bits + * in the button byte. + * [dhh 02/10/08] + */ + err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); + DPRINTF(("%s: attach: report descriptor size = %d\n", + USBDEVNAME(sc->sc_dev),size)); if (err) USB_ATTACH_ERROR_RETURN; - if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), - hid_input, &sc->sc_loc_x, &flags)) { + /* + * sc->sc_iid is taken from the hid_item structure, which is filled in by + * hid_hlocate(). Each of the following calls should get the same report id. + * This allows hid_reportID_size() to compute the correct data report size + * when there is more than one report id in the descriptor. + * [dhh 02/10/08] + */ + if (!hid_hlocate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + hid_input, &h)) { printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } - if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { - printf("%s: X report 0x%04x not supported\n", - USBDEVNAME(sc->sc_dev), flags); - USB_ATTACH_ERROR_RETURN; + else + { + sc->sc_loc_x = h.loc; + sc->sc_iid = item_id = h.report_ID; /* The report ID */ + xflags = h.flags; + + if ((xflags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { + printf("%s: non-relative X report 0x%04x not supported\n", + USBDEVNAME(sc->sc_dev), xflags); + USB_ATTACH_ERROR_RETURN; + } + DPRINTF(("%s: attach: X (flags 0x%02x) item_id=%d, sc_loc_x.pos=%d/%d\n", + USBDEVNAME(sc->sc_dev),xflags,item_id, + sc->sc_loc_x.pos,sc->sc_loc_x.size)); } - if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), - hid_input, &sc->sc_loc_y, &flags)) { + if (!hid_hlocate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), + hid_input, &h)) { printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } - if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { - printf("%s: Y report 0x%04x not supported\n", - USBDEVNAME(sc->sc_dev), flags); - USB_ATTACH_ERROR_RETURN; + else + { + sc->sc_loc_y = h.loc; + yflags = h.flags; + if(item_id != h.report_ID) /* all item_ids should be the same */ + { + DPRINTF(("%s: attach: WARNING: report ID changed for Y\n", + USBDEVNAME(sc->sc_dev))); + item_id = h.report_ID; + } + + if ((yflags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { + printf("%s: non-relative Y report 0x%04x not supported\n", + USBDEVNAME(sc->sc_dev), yflags); + USB_ATTACH_ERROR_RETURN; + } + DPRINTF(("%s: attach: Y (flags 0x%02x) item_id=%d, sc_loc_y.pos=%d/%d\n", + USBDEVNAME(sc->sc_dev),yflags,item_id, + sc->sc_loc_y.pos,sc->sc_loc_y.size)); } - /* try to guess the Z activator: first check Z, then WHEEL */ - if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), - hid_input, &sc->sc_loc_z, &flags) || - hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), - hid_input, &sc->sc_loc_z, &flags) || - hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), - hid_input, &sc->sc_loc_z, &flags)) { - if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { - sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ - } else { - sc->flags |= UMS_Z; + if (!hid_hlocate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), + hid_input, &h)) { + no_report |= UMS_Z; + } + else + { + sc->sc_loc_z = h.loc; + zflags = h.flags; + if(item_id != h.report_ID) /* all item_ids should be the same */ + { + DPRINTF(("%s: attach: WARNING: report ID changed for Z\n", + USBDEVNAME(sc->sc_dev))); + item_id = h.report_ID; + } + if ((zflags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { + no_support |= UMS_Z; } + DPRINTF(("%s: attach: Z (flags 0x%02x) item_id=%d, sc_loc_z.pos=[0x%x]%d/%d\n", + USBDEVNAME(sc->sc_dev),zflags,item_id, + sc->sc_loc_z.pos,sc->sc_loc_z.pos,sc->sc_loc_z.size)); } - /* The Microsoft Wireless Intellimouse 2.0 reports it's wheel - * using 0x0048 (i've called it 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(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), - hid_input, &sc->sc_loc_t, &flags)) { - sc->sc_loc_t.pos = sc->sc_loc_t.pos + 8; - sc->flags |= UMS_T; + if (!hid_hlocate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), + hid_input, &h)) + { + no_report |= UMS_W; + } + else + { + sc->sc_loc_w = h.loc; + wflags = h.flags; + if(item_id != h.report_ID) /* all item_ids should be the same */ + { + DPRINTF(("%s: attach: WARNING: report ID changed for W\n", + USBDEVNAME(sc->sc_dev))); + item_id = h.report_ID; + } + if ((wflags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { + no_support |= UMS_W; + } + DPRINTF(("%s: attach: W (flags 0x%02x) item_id=%d, sc_loc_w.pos=%d/%d\n", + USBDEVNAME(sc->sc_dev),wflags,item_id, + sc->sc_loc_w.pos,sc->sc_loc_w.size)); + } + + if (!hid_hlocate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), + hid_input, &h)) { + no_report |= UMS_T; + } + else + { + sc->sc_loc_t = h.loc; + tflags = h.flags; + if(item_id != h.report_ID) /* all item_ids should be the same */ + { + DPRINTF(("%s: attach: WARNING: report ID changed for T\n", + USBDEVNAME(sc->sc_dev))); + item_id = h.report_ID; + } + if ((tflags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { + no_support |= UMS_T; + } + DPRINTF(("%s: attach: T (flags 0x%02x) item_id=%d, sc_loc_t.pos=%d/%d\n", + USBDEVNAME(sc->sc_dev),tflags,item_id, + sc->sc_loc_t.pos,sc->sc_loc_t.size)); } + + /* This driver supports mice which deliver relative data (each reported value + * is relative to its last value). Devices which deliver absolute data are + * in the wrong place. + */ + if(no_report & UMS_Z) + printf("%s: mouse has no Z report\n", USBDEVNAME(sc->sc_dev)); + if(no_support & UMS_Z) + printf("%s: non-relative Z report not supported by this driver\n", + USBDEVNAME(sc->sc_dev)); + if(no_report & UMS_T) + printf("%s: mouse has no T report\n", USBDEVNAME(sc->sc_dev)); + if(no_support & UMS_T) + printf("%s: non-relative T report not supported by this driver\n", + USBDEVNAME(sc->sc_dev)); + if(no_report & UMS_W) + printf("%s: mouse has no W report\n", USBDEVNAME(sc->sc_dev)); + if(no_support & UMS_W) + printf("%s: non-relative W report not supported by this driver\n", + USBDEVNAME(sc->sc_dev)); + /* figure out the number of buttons */ for (i = 1; i <= MAX_BUTTONS; i++) - if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), - hid_input, &loc_btn, 0)) + if (!hid_hlocate(desc, size, HID_USAGE2(HUP_BUTTON, i), + hid_input, &h)) break; sc->nbuttons = i - 1; sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, @@ -293,15 +589,18 @@ USB_ATTACH_ERROR_RETURN; } - printf("%s: %d buttons%s%s.\n", USBDEVNAME(sc->sc_dev), - sc->nbuttons, sc->flags & UMS_Z? " and Z dir" : "", - sc->flags & UMS_T?" and a TILT dir": ""); - + /* Locate all of the button bits the mouse claims */ for (i = 1; i <= sc->nbuttons; i++) - hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), - hid_input, &sc->sc_loc_btn[i-1], 0); + { + hid_hlocate(desc, size, HID_USAGE2(HUP_BUTTON, i), + hid_input, &h); + sc->sc_loc_btn[i-1] = h.loc; + } - sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); + /* get the report size for the id filled in by hid_hlocate() */ + sc->sc_isize = hid_reportID_size(desc, size, hid_input, sc->sc_iid); + printf("%s: input report ID=%d/0x%x, report size %d.\n", + USBDEVNAME(sc->sc_dev), sc->sc_iid,sc->sc_iid,sc->sc_isize); sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT); if (!sc->sc_ibuf) { printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); @@ -309,26 +608,207 @@ USB_ATTACH_ERROR_RETURN; } + /* + * If multiple reports are in the descriptor, the bit positions of data + * items within the input report may be computed by simply subtracting + * the starting position of the identified report ID from the starting + * position of the input report generated from the descriptor string. + * The first mouse button is always the start of the report id within the + * descriptor, but data reports with non-zero IDs are always preceded by + * an id byte, so the button byte will start at bit 8. + * + * If the report id is 0, there is only one input data report, with no + * report ID and with no leading id byte in the report data. In that + * case, position data is correct as reported. + * + * [dhh 02/10/08] + */ + + if(sc->sc_iid) + { + /* This should be correct even if our iid is first in the + * list, because then we need to *add* 8 to account for the + * report ID. (Subtracting -8 is just as good). + */ + id_offset = sc->sc_loc_btn[0].pos - 8; +DPRINTF(("%s: attach: id_offset == %d\n",USBDEVNAME(sc->sc_dev),id_offset)); + sc->sc_loc_x.pos -= id_offset; + sc->sc_loc_y.pos -= id_offset; + if(sc->sc_loc_z.pos > id_offset) + sc->sc_loc_z.pos -= id_offset; + if(sc->sc_loc_t.pos > id_offset) + sc->sc_loc_t.pos -= id_offset; + if(sc->sc_loc_w.pos > id_offset) + sc->sc_loc_w.pos -= id_offset; + for(i = 0; i < sc->nbuttons; ++i) + if(sc->sc_loc_btn[i].pos > id_offset) + sc->sc_loc_btn[i].pos -= id_offset; + + for(i = 0; i < sc->nbuttons; ++i) + DPRINTF(("%s: attach: fixed B%d ==> %2d/%d\n", + USBDEVNAME(sc->sc_dev), i, + sc->sc_loc_btn[i].pos, sc->sc_loc_btn[i].size)); +DPRINTF(("%s: attach: fixed X ==> %2d/%d\n", + USBDEVNAME(sc->sc_dev), sc->sc_loc_x.pos, sc->sc_loc_x.size)); +DPRINTF(("%s: attach: fixed Y ==> %2d/%d\n", + USBDEVNAME(sc->sc_dev), sc->sc_loc_y.pos, sc->sc_loc_y.size)); +DPRINTF(("%s: attach: fixed Z ==> %2d/%d\n", + USBDEVNAME(sc->sc_dev), sc->sc_loc_z.pos, sc->sc_loc_z.size)); +DPRINTF(("%s: attach: fixed T ==> %2d/%d\n", + USBDEVNAME(sc->sc_dev), sc->sc_loc_t.pos, sc->sc_loc_t.size)); +DPRINTF(("%s: attach: fixed W ==> %2d/%d\n", + USBDEVNAME(sc->sc_dev), sc->sc_loc_w.pos, sc->sc_loc_w.size)); + } + + /* + * Wheel and tilt wheel data, if present, should be written as + * Z axis data. If there is tilt data (the mouse responded + * on HUG_TWHEEL rather than HUG_WHEEL) the tilt information + * should appear on the next byte after Z. + * + * [dhh 02/10/08] + */ + + if(sc->sc_loc_z.size == 0) + { + /* + * This assumes there will be W OR T, not both. + */ + if(sc->sc_loc_w.size) + { + /* Move the wheel data to the Z output axis */ + DPRINTF(("%s: attach: W.pos => Z.pos\n",USBDEVNAME(sc->sc_dev))); + sc->sc_loc_z.pos = sc->sc_loc_w.pos; + sc->sc_loc_z.size = sc->sc_loc_w.size; + sc->flags = UMS_Z|UMS_W; + sc->sc_loc_w.pos = sc->sc_loc_w.size = 0; + } + else if(sc->sc_loc_t.size) + { + /* Move the wheel data to the Z output axis */ + DPRINTF(("%s: attach: T.pos => Z.pos\n",USBDEVNAME(sc->sc_dev))); + sc->sc_loc_z.pos = sc->sc_loc_t.pos; + sc->sc_loc_z.size = sc->sc_loc_t.size; + sc->sc_loc_w.pos = sc->sc_loc_w.size = 0; + sc->flags = UMS_Z|UMS_T; + sc->sc_loc_t.pos += 8; + DPRINTF(("%s: attach: T.pos = Z.pos + 8, sc_isize=%d\n", + USBDEVNAME(sc->sc_dev),sc->sc_isize)); + } + } + else + { + /* Reports are not expected on the Z *and* wheel usages. + * Note the wheel usages and report them, leaving Z alone. + * If this happens, someone should be told... + */ + if(sc->sc_loc_w.size) + { + printf("%s: attach: found Z *and* W data...", + USBDEVNAME(sc->sc_dev)); + printf("using Z data only\n"); + sc->sc_loc_w.pos =sc->sc_loc_w.size = 0; + } + if(sc->sc_loc_t.size) + { + printf("%s: attach: found Z *and* T data...", + USBDEVNAME(sc->sc_dev)); + printf("using Z data only\n"); + sc->sc_loc_t.pos =sc->sc_loc_t.size = 0; + } + sc->flags = UMS_Z; + } + + /* + * Now check for quirks. + */ + if (usbd_get_quirks(uaa->device)->uq_flags & UQ_MS_REVZ) { + /* Some wheels need the Z axis reversed. */ + if(sc->flags & UMS_Z) + { + sc->flags |= UMS_REVZ; + DPRINTF(("attach: add UMS_REVZ to sc->flags for wheel -> 0x%x\n",sc->flags)); + } + } + if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) { + DPRINTF(("%s: Spurious button up events\n", + USBDEVNAME(sc->sc_dev))); + sc->flags |= UMS_SPUR_BUT_UP; + } + + /* + * If get_protocol claimed protocol 0, and it is true, then the exploration + * above should have found the truth in the report descriptor and the report + * size should be 3. If it's greater than 3, then get_protocol() lied and it + * seems best to believe the report descriptor. If size is less than 3 in + * in the report descriptor, we can try assuming that the protocol really is + * BOOT, but in fact this is a "firewall" which is not likely to be taken, + * or to succeed if it is taken. It may be useful for a non-conforming mouse. + * [dhh 02/10/08] + */ + if((protocol == 0) && (sc->sc_isize < 3)) + { + DPRINTF(("Configuring BOOT input protocol\n")); + sc->flags = 0; + sc->sc_loc_z.pos = sc->sc_loc_t.pos = sc->sc_loc_w.pos = 0; + sc->sc_loc_z.size = sc->sc_loc_t.size = sc->sc_loc_w.size = 0; + sc->sc_isize = 3; + sc->sc_loc_x.pos = 8; + sc->sc_loc_y.pos = 16; + sc->sc_loc_btn[0].pos = 0; + sc->sc_loc_btn[1].pos = 1; + sc->sc_loc_btn[2].pos = 2; + sc->sc_loc_btn[0].size = 1; + sc->sc_loc_btn[1].size = 1; + sc->sc_loc_btn[2].size = 1; + sc->nbuttons = 3; + sc->sc_iid = 0; + } + + printf("%s: %d buttons%s%s%s%s.\n", USBDEVNAME(sc->sc_dev), + sc->nbuttons, sc->flags & UMS_Z? " and Z dir" : " and X,Y only", + sc->flags & UMS_REVZ? " (reversed)" : "", + sc->flags & UMS_W? " (wheel)" : "", + sc->flags & UMS_T?" (TILT wheel)": ""); + + sc->sc_ep_addr = ed->bEndpointAddress; sc->sc_disconnected = 0; - free(desc, M_TEMP); + sc->sc_repdesc = desc; + sc->sc_repdesc_size = size; #ifdef USB_DEBUG - DPRINTF(("ums_attach: sc=%p\n", sc)); - DPRINTF(("ums_attach: X\t%d/%d\n", - sc->sc_loc_x.pos, sc->sc_loc_x.size)); - DPRINTF(("ums_attach: Y\t%d/%d\n", - sc->sc_loc_y.pos, sc->sc_loc_y.size)); - if (sc->flags & UMS_Z) - DPRINTF(("ums_attach: Z\t%d/%d\n", - sc->sc_loc_z.pos, sc->sc_loc_z.size)); + DPRINTF(("%s: ATTACH: sc=%p\n",USBDEVNAME(sc->sc_dev), sc)); + if(sc->sc_iid) + DPRINTF(("%s: ATTACH: ID 0/8\n",USBDEVNAME(sc->sc_dev))); for (i = 1; i <= sc->nbuttons; i++) { - DPRINTF(("ums_attach: B%d\t%d/%d\n", + DPRINTF(("%s: ATTACH: B%d %2d/%d\n",USBDEVNAME(sc->sc_dev), i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); } - DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); + DPRINTF(("%s: ATTACH: X %2d/%d\n",USBDEVNAME(sc->sc_dev), + sc->sc_loc_x.pos, sc->sc_loc_x.size)); + DPRINTF(("%s: ATTACH: Y %2d/%d\n",USBDEVNAME(sc->sc_dev), + sc->sc_loc_y.pos, sc->sc_loc_y.size)); + DPRINTF(("%s: ATTACH: Z %2d/%d\n",USBDEVNAME(sc->sc_dev), + sc->sc_loc_z.pos, sc->sc_loc_z.size)); + DPRINTF(("%s: ATTACH: T %2d/%d\n",USBDEVNAME(sc->sc_dev), + sc->sc_loc_t.pos, sc->sc_loc_t.size)); + DPRINTF(("%s: ATTACH: W %2d/%d\n",USBDEVNAME(sc->sc_dev), + sc->sc_loc_w.pos, sc->sc_loc_w.size)); + DPRINTF(("%s: ATTACH: isize = %d, iid = %d\n",USBDEVNAME(sc->sc_dev), + sc->sc_isize, sc->sc_iid)); #endif + /* Set up the output protocol. Defaults to sysmouse level 0, + * which uses the 5-byte MSC protocol, and is incapable of + * presenting Z or tilt data to the display driver. + * The application may override this by issuing an ioctl to + * set the level to 1, which selects the "sysmouse" protocol, + * which can handle wheel data, or 3, which establishes an + * "extended" sysmous protocol with room for tilt data as well. + * + * [dhh 02/10/08] + */ if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else @@ -349,6 +829,7 @@ sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; + sc->status.dt = 0; #ifndef __FreeBSD__ sc->rsel.si_flags = 0; @@ -360,11 +841,6 @@ 0644, "ums%d", device_get_unit(self)); usb_callout_init(sc->callout_handle); - if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) { - DPRINTF(("%s: Spurious button up events\n", - USBDEVNAME(sc->sc_dev))); - sc->flags |= UMS_SPUR_BUT_UP; - } USB_ATTACH_SUCCESS_RETURN; } @@ -378,7 +854,7 @@ if (sc->sc_enabled) ums_disable(sc); - DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); + DPRINTFN(2,("%s: disconnected\n", USBDEVNAME(self))); free(sc->sc_loc_btn, M_USB); free(sc->sc_ibuf, M_USB); @@ -402,14 +878,14 @@ destroy_dev(sc->dev); + if (sc->sc_repdesc) + free(sc->sc_repdesc, M_USBDEV); + return 0; } void -ums_intr(xfer, addr, status) - usbd_xfer_handle xfer; - usbd_private_handle addr; - usbd_status status; +ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { struct ums_softc *sc = addr; u_char *ibuf; @@ -419,17 +895,21 @@ #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) - DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); - DPRINTFN(5, ("ums_intr: data =")); + DPRINTFN(2, ("%s: ums_intr: sc=%p id=%d status=%d\n", + USBDEVNAME(sc->sc_dev), sc, sc->sc_iid, status)); + DPRINTFN(2, ("%s: ums_intr: data =",USBDEVNAME(sc->sc_dev))); for (i = 0; i < sc->sc_isize; i++) - DPRINTFN(5, (" %02x", sc->sc_ibuf[i])); - DPRINTFN(5, ("\n")); + DPRINTFN(2, (" 0x%02x", sc->sc_ibuf[i])); + DPRINTFN(2,(" sc_iid = %d/0x%x",sc->sc_iid,sc->sc_iid)); if (status == USBD_CANCELLED) + { + DPRINTFN(3, (" CANCELLED\n")); return; + } if (status != USBD_NORMAL_COMPLETION) { - DPRINTF(("ums_intr: status=%d\n", status)); + DPRINTFN(3, ("\n")); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_intrpipe); if(status != USBD_IOERROR) @@ -437,32 +917,45 @@ } ibuf = sc->sc_ibuf; - /* - * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte of - * data compared to most USB mice. This byte frequently switches - * from 0x01 (usual state) to 0x02. I assume it is to allow - * extra, non-standard, reporting (say battery-life). However - * at the same time it generates a left-click message on the button - * byte which causes spurious left-click's where there shouldn't be. - * This should sort that. - * Currently it's the only user of UMS_T so use it as an identifier. - * We probably should switch to some more official quirk. - */ - if (sc->flags & UMS_T) { - if (sc->sc_iid) { - if (*ibuf++ == 0x02) - return; - } - } else { - if (sc->sc_iid) { - if (*ibuf++ != sc->sc_iid) - return; - } + + /* A leading byte giving report_ID is present for devices which have + * multiple report ids (most Microsoft mice, e.g.). If the + * leading byte does not match sc_iid, the data is for some other + * report and should be ignored. Mice which have just a single + * report (Logitech...) have no report_ID and the data is always + * accepted. + * + * [dhh 02/10/08] + */ + + if ((sc->sc_iid) && (*ibuf != sc->sc_iid)) + { + /* + * The Microsoft Wireless Notebook Optical Mouse 3000 + * (model 1056) fails to respond to set_idle(interface,0,0) + * and interrupts continuously at the polling rate with + * reports for ID 0x15 (21). It can be ignored here + * (0x11 is the ID which carries the data we want). + * + * [dhh 02/10/08] + */ + DPRINTFN(3,(" IGNORE iid %d/0x%x, sc=%p\n", *ibuf, *ibuf, sc)); + return; } + DPRINTFN(3, ("\n")); dx = hid_get_data(ibuf, &sc->sc_loc_x); dy = -hid_get_data(ibuf, &sc->sc_loc_y); - dz = -hid_get_data(ibuf, &sc->sc_loc_z); + if(sc->flags & UMS_Z) + dz = -hid_get_data(ibuf, &sc->sc_loc_z); + else + dz = 0; + if (sc->flags & UMS_REVZ) + dz = -dz; + + /* + * tilt data shows up here as +/- 1, separate from the buttons + */ if (sc->flags & UMS_T) dt = -hid_get_data(ibuf, &sc->sc_loc_t); else @@ -473,18 +966,19 @@ if (dx || dy || dz || dt || (sc->flags & UMS_Z) || buttons != sc->status.button) { - DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d t:%d buttons:0x%x\n", - dx, dy, dz, dt, buttons)); + DPRINTFN(2, ("%s: ums_intr: x:%d y:%d z:%d t:%d buttons:0x%x\n", + USBDEVNAME(sc->sc_dev), dx, dy, dz, dt, buttons)); sc->status.button = buttons; sc->status.dx += dx; sc->status.dy += dy; sc->status.dz += dz; - /* sc->status.dt += dt;*/ /* no way to export this yet */ + sc->status.dt += dt; /* Discard data in case of full buffer */ if (sc->qcount == sizeof(sc->qbuf)) { - DPRINTF(("Buffer full, discarded packet")); + DPRINTF(("%s: ums_intr: buffer full, discarded packet, sc=%p\n", + USBDEVNAME(sc->sc_dev),sc)); return; } @@ -524,7 +1018,7 @@ { /* Discard data in case of full buffer */ if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) { - DPRINTF(("Buffer full, discarded packet")); + DPRINTF(("ums_add_to_queue: buffer full, discarded packet\n")); return; } @@ -537,6 +1031,11 @@ if (dt > 126) dt = 126; if (dt < -128) dt = -128; + /* This is the sysmouse protocol and is unable to send + * tilt data, even at level 1, because it needs 2 bytes + * for each axis, and 2 for buttons. + */ + sc->qbuf[sc->qhead] = sc->mode.syncmask[1]; sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS; sc->qbuf[sc->qhead+1] = dx >> 1; @@ -544,12 +1043,35 @@ sc->qbuf[sc->qhead+3] = dx - (dx >> 1); sc->qbuf[sc->qhead+4] = dy - (dy >> 1); - if (sc->mode.level == 1) { + DPRINTFN(2,(" %02x %02x %02x %02x %02x", + sc->qbuf[sc->qhead], + sc->qbuf[sc->qhead+1], + sc->qbuf[sc->qhead+2], + sc->qbuf[sc->qhead+3], + sc->qbuf[sc->qhead+4])); + /* level 2 is "native" mode in the PS/2 driver; ignore level 2 */ + if ((sc->mode.level == 1) || (sc->mode.level == 3)) { sc->qbuf[sc->qhead+5] = dz >> 1; sc->qbuf[sc->qhead+6] = dz - (dz >> 1); sc->qbuf[sc->qhead+7] = ((~buttons >> 3) & MOUSE_SYS_EXTBUTTONS); + DPRINTFN(2,(" %02x %02x %02x", + sc->qbuf[sc->qhead+5], + sc->qbuf[sc->qhead+6], + sc->qbuf[sc->qhead+7])); + if (sc->mode.level == 3) { + sc->qbuf[sc->qhead+8] = dt >> 1; + sc->qbuf[sc->qhead+9] = dt - (dt >> 1); + sc->qbuf[sc->qhead+10] = 0; + sc->qbuf[sc->qhead+11] = 0; + DPRINTFN(2,(" %02x %02x %02x %02x", + sc->qbuf[sc->qhead+8], + sc->qbuf[sc->qhead+9], + sc->qbuf[sc->qhead+10], + sc->qbuf[sc->qhead+11])); + } } + DPRINTFN(2,("\n")); sc->qhead += sc->mode.packetsize; sc->qcount += sc->mode.packetsize; @@ -567,6 +1089,7 @@ selwakeuppri(&sc->rsel, PZERO); } } + Static int ums_enable(v) void *v; @@ -575,6 +1098,7 @@ usbd_status err; + DPRINTFN(2,("%s: enable\n", USBDEVNAME(sc->sc_dev))); if (sc->sc_enabled) return EBUSY; @@ -583,7 +1107,8 @@ sc->qhead = sc->qtail = 0; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; - sc->status.dx = sc->status.dy = sc->status.dz /* = sc->status.dt */ = 0; + sc->status.dx = sc->status.dy = sc->status.dz = 0; + sc->status.dt = 0; callout_handle_init((struct callout_handle *)&sc->callout_handle); @@ -614,17 +1139,27 @@ usbd_close_pipe(sc->sc_intrpipe); sc->sc_enabled = 0; + DPRINTFN(2,("%s: disabled\n", USBDEVNAME(sc->sc_dev))); if (sc->qcount != 0) - DPRINTF(("Discarded %d bytes in queue\n", sc->qcount)); + DPRINTF(("ums_disable Discarded %d bytes in queue for %p\n", sc->qcount,sc)); } Static int ums_open(struct cdev *dev, int flag, int fmt, usb_proc_ptr p) { struct ums_softc *sc; + usbd_status ustatus; USB_GET_SC_OPEN(ums, UMSUNIT(dev), sc); + if (sc == NULL) + return (ENXIO); + DPRINTFN(2,("%s: open\n", USBDEVNAME(sc->sc_dev))); + if((ustatus = usbd_set_idle(sc->sc_iface,0,0))) + { + printf("%s: usbd_set_idle(interface,0,0) FAILED, status %d\n", + USBDEVNAME(sc->sc_dev),ustatus); + } return ums_enable(sc); } @@ -638,6 +1173,7 @@ if (!sc) return 0; + DPRINTFN(2,("%s: close\n", USBDEVNAME(sc->sc_dev))); if (sc->sc_enabled) ums_disable(sc); @@ -741,11 +1277,25 @@ return revents; } +/* This function now sports several additional ioctl calls (similar to those + * supplied by the ugen driver) which allows user level programs read access + * to information. It is likely that other ioctls could be useful. + * + * Enquiring minds want to know... + * + * [dhh 02/10/08] see "mouse_report" + */ + int ums_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct ums_softc *sc; + struct usb_ctl_report_desc *rd; + usb_interface_descriptor_t *iface_desc; + struct usb_string_desc *string_desc; + usbd_status err; int error = 0; + int size; int s; mousemode_t mode; @@ -757,17 +1307,21 @@ switch(cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->hw; + DPRINTFN(1,("ums_ioctl: MOUSE_GETHWINFO\n")); break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->mode; + DPRINTFN(1,("ums_ioctl: MOUSE_GETMODE: mode.level is %d for sc=%p\n",sc->mode.level,sc)); break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; - if (mode.level == -1) + DPRINTFN(1,("ums_ioctl: MOUSE_SETMODE: set mode.level to %d for sc=%p\n",mode.level,sc)); + /* Level 2 is used by the PS/2 driver for "native mode" */ + if ((mode.level == -1) || (mode.level == 2)) /* don't change the current setting */ - ; - else if ((mode.level < 0) || (mode.level > 1)) + mode.level = sc->mode.level; + else if ((mode.level < 0) || (mode.level > 3)) return (EINVAL); s = splusb(); @@ -791,7 +1345,17 @@ sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; + } else if (sc->mode.level == 3) { + if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) + sc->hw.buttons = MOUSE_SYS_MAXBUTTON; + else + sc->hw.buttons = sc->nbuttons; + sc->mode.protocol = MOUSE_PROTO_SYSMOUSE_EXT; + sc->mode.packetsize = MOUSE_SYS_EXT_PACKETSIZE; + sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } + DPRINTFN(1,("ums_ioctl: MOUSE_SETMODE: mode.level set to %d for sc=%p\n",sc->mode.level,sc)); bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; @@ -800,9 +1364,11 @@ break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; + DPRINTFN(1,("ums_ioctl: MOUSE_GETLEVEL: mode.level is %d for sc=%p\n",sc->mode.level,sc)); break; case MOUSE_SETLEVEL: - if (*(int *)addr < 0 || *(int *)addr > 1) + DPRINTFN(1,("ums_ioctl: MOUSE_SETLEVEL: set mode.level to %d for sc=%p\n",sc->mode.level,sc)); + if (*(int *)addr < 0 || *(int *)addr > 3) return (EINVAL); s = splusb(); @@ -826,7 +1392,17 @@ sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; + } else if (sc->mode.level == 3) { + if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) + sc->hw.buttons = MOUSE_SYS_MAXBUTTON; + else + sc->hw.buttons = sc->nbuttons; + sc->mode.protocol = MOUSE_PROTO_SYSMOUSE_EXT; + sc->mode.packetsize = MOUSE_SYS_EXT_PACKETSIZE; + sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } + DPRINTFN(1,("ums_ioctl: MOUSE_SETLEVEL: mode.level set to %d for sc=%p\n",sc->mode.level,sc)); bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; @@ -840,16 +1416,61 @@ *status = sc->status; sc->status.obutton = sc->status.button; sc->status.button = 0; - sc->status.dx = sc->status.dy - = sc->status.dz = /* sc->status.dt = */ 0; + sc->status.dx = sc->status.dy = sc->status.dz = 0; + sc->status.dt = 0; splx(s); - if (status->dx || status->dy || status->dz /* || status->dt */) + if (status->dx || status->dy || status->dz || status->dt) status->flags |= MOUSE_POSCHANGED; if (status->button != status->obutton) status->flags |= MOUSE_BUTTONSCHANGED; break; } + case USB_GET_DEVICEINFO: + usbd_fill_deviceinfo(sc->sc_device, + (struct usb_device_info *)addr, 1); + break; + case USB_GET_DEVICE_DESC: + *(usb_device_descriptor_t *)addr = + *usbd_get_device_descriptor(sc->sc_device); + break; + case USB_GET_INTERFACE_DESC: + iface_desc = usbd_get_interface_descriptor(sc->sc_iface); + if(iface_desc) + *(usb_interface_descriptor_t *)addr = *iface_desc; + break; + case USB_GET_REPORT_DESC: + rd = (struct usb_ctl_report_desc *)addr; + size = min(sc->sc_repdesc_size, sizeof rd->ucrd_data); + rd->ucrd_size = size; + /* The report descriptor is saved at attach */ + memcpy(rd->ucrd_data, sc->sc_repdesc, size); + break; + case USB_GET_STRING_DESC: { + int len; + string_desc = (struct usb_string_desc *)addr; + if (usbd_get_quirks(sc->sc_device)->uq_flags & UQ_NO_STRINGS) + return (USBD_STALLED); + if (sc->sc_device->langid == -1) + { + /* Set up default language */ + err = usbd_get_string_desc(sc->sc_device, USB_LANGUAGE_TABLE, 0, + &string_desc->usd_desc, + &size); + if (err || size < 4) { + DPRINTFN(-1,("usbd_get_string: getting lang failed, using 0\n")); + sc->sc_device->langid = 0; /* Well, just pick something then */ + } else { + /* Pick the first language as the default. */ + sc->sc_device->langid = UGETW(string_desc->usd_desc.bString[0]); + } + } + err = usbd_get_string_desc(sc->sc_device, string_desc->usd_string_index, + sc->sc_device->langid, &string_desc->usd_desc, &len); + if (err) + return (EINVAL); + } + break; default: error = ENOTTY; } --- usb_subr.c.orig 2008-02-28 16:01:37.000000000 -0800 +++ usb_subr.c 2008-02-28 14:39:40.000000000 -0800 @@ -9,6 +9,7 @@ * $NetBSD: usb_subr.c,v 1.116 2004/06/23 06:27:54 mycroft Exp $ * $NetBSD: usb_subr.c,v 1.119 2004/10/23 13:26:33 augustss Exp $ */ +/* UMSPATCH-dhh-1a 02/24/08 */ #include __FBSDID("$FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.76.2.3 2006/03/01 01:59:05 iedowse Exp $"); @@ -81,7 +82,8 @@ #endif #ifdef USB_DEBUG -#define DPRINTF(x) if (usbdebug) logprintf x +/* ###%%% fix this */ +#define DPRINTF(x) if (usbdebug >= 0) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x extern int usbdebug; #else @@ -103,6 +105,7 @@ Static void usbd_kill_pipe(usbd_pipe_handle); Static usbd_status usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, int port, int addr); +Static void print_usbdev_iface(usb_interface_descriptor_t *idesc,int ifaceno); Static u_int32_t usb_cookie_no = 0; @@ -805,6 +808,28 @@ return (-1); } +/* Print the interface description at any non-zero debug level. This is */ +/* information which may be needed in the match routines, especially for devices */ +/* with multiple interfaces. */ + +void +print_usbdev_iface(usb_interface_descriptor_t *idesc,int ifaceno) +{ + if(idesc != NULL) + { + DPRINTF(("Interface %u:\n",ifaceno)); + DPRINTF((": bLength = 0x%x\n",idesc->bLength)); + DPRINTF((": bDescriptorType = 0x%x\n",idesc->bDescriptorType)); + DPRINTF((": bInterfaceNumber = 0x%x\n",idesc->bInterfaceNumber)); + DPRINTF((": bAlternateSetting = 0x%x\n",idesc->bAlternateSetting)); + DPRINTF((": bNumEndpoints = 0x%x\n",idesc->bNumEndpoints)); + DPRINTF((": bInterfaceClass = 0x%x\n",idesc->bInterfaceClass)); + DPRINTF((": bInterfaceSubClass = 0x%x\n",idesc->bInterfaceSubClass)); + DPRINTF((": bInterfaceProtocol = 0x%x\n",idesc->bInterfaceProtocol)); + DPRINTF((": iInterface = 0x%x\n\n",idesc->iInterface)); + } +} + usbd_status usbd_probe_and_attach(device_ptr_t parent, usbd_device_handle dev, @@ -817,6 +842,7 @@ device_ptr_t dv; device_ptr_t *tmpdv; usbd_interface_handle ifaces[256]; /* 256 is the absolute max */ + char devinfo[1024]; #if defined(__FreeBSD__) /* XXX FreeBSD may leak resources on failure cases -- fixme */ @@ -828,13 +854,13 @@ device_printf(parent, "Device creation failed\n"); return (USBD_INVAL); } - device_quiet(bdev); uaap = malloc(sizeof(uaa), M_USB, M_NOWAIT); if (uaap == NULL) { return (USBD_INVAL); } device_set_ivars(bdev, uaap); #endif + uaa.device = dev; uaa.iface = NULL; uaa.ifaces = NULL; @@ -847,6 +873,10 @@ uaa.product = UGETW(dd->idProduct); uaa.release = UGETW(dd->bcdDevice); + DPRINTF(("\nusbd_probe_and_attach: idVendor = 0x%x\n",uaa.vendor & 0xffff)); + DPRINTF((": : idProduct = 0x%x\n",uaa.product & 0xffff)); + DPRINTF((": : bcdDevice (release) = 0x%x\n\n",uaa.release & 0xffff)); + /* First try with device specific drivers. */ DPRINTF(("usbd_probe_and_attach: trying device specific drivers\n")); @@ -890,8 +920,12 @@ } nifaces = dev->cdesc->bNumInterface; uaa.configno = dev->cdesc->bConfigurationValue; + DPRINTF(("usbd_probe_and_attach: looping over %d interfaces\n",nifaces)); for (i = 0; i < nifaces; i++) + { ifaces[i] = &dev->ifaces[i]; + print_usbdev_iface(ifaces[i]->idesc,i); + } uaa.ifaces = ifaces; uaa.nifaces = nifaces; dev->subdevs = malloc((nifaces+1) * sizeof dv, M_USB,M_NOWAIT); @@ -914,10 +948,15 @@ dev->subdevs[found + 1] = 0; dev->ifacenums[found] = i; *uaap = uaa; + usbd_devinfo(dev, 3, devinfo); + + DPRINTF(("usbd_probe_and_attach: check interface %d\n",i)); + DPRINTF(("usbd_probe_and_attach: bInterfaceNumber = %d\n",uaa.ifaceno)); dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch); if (dv != NULL) { - ifaces[i] = 0; /* consumed */ + ifaces[i] = 0; /* consumed */ + DPRINTF(("usbd_probe_and_attach: FOUND interface %d\n",i)); found++; #if defined(__FreeBSD__) @@ -933,7 +972,6 @@ return (USBD_NOMEM); } device_set_ivars(bdev, uaap); - device_quiet(bdev); #endif } else { dev->subdevs[found] = 0; @@ -942,6 +980,7 @@ if (found != 0) { #if defined(__FreeBSD__) /* remove the last created child. It is unused */ + free(uaap, M_USB); device_delete_child(parent, bdev); /* free(uaap, M_USB); */ /* May be needed? xxx */ #endif --- hid.h.orig 2008-02-28 16:01:37.000000000 -0800 +++ hid.h 2008-02-28 14:39:40.000000000 -0800 @@ -1,5 +1,6 @@ /* $NetBSD: hid.h,v 1.6 2000/06/01 14:28:57 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/hid.h,v 1.13 2005/01/06 01:43:27 imp Exp $ */ +/* UMSPATCH-dhh-1a 02/24/08 */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -84,8 +85,13 @@ void hid_end_parse(struct hid_data *s); int hid_get_item(struct hid_data *s, struct hid_item *h); int hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *id); +int hid_reportID_size(void *buf, int len, enum hid_kind k, u_int8_t report_id); + int hid_locate(void *desc, int size, u_int32_t usage, enum hid_kind kind, struct hid_location *loc, u_int32_t *flags); +int hid_hlocate(void *desc, int size, u_int32_t usage, + enum hid_kind kind, struct hid_item *h); + u_long hid_get_data(u_char *buf, struct hid_location *loc); int hid_is_collection(void *desc, int size, u_int32_t usage); --- usb.h.orig 2008-02-28 16:01:37.000000000 -0800 +++ usb.h 2008-02-28 14:39:40.000000000 -0800 @@ -1,5 +1,6 @@ /* $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb.h,v 1.39.2.2 2007/06/17 09:38:27 brueffer Exp $ */ +/* UMSPATCH-dhh-1a 02/24/08 */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -422,6 +423,7 @@ #define UICLASS_HID 0x03 #define UISUBCLASS_BOOT 1 #define UIPROTO_BOOT_KEYBOARD 1 +#define UIPROTO_MOUSE 2 #define UICLASS_PHYSICAL 0x05