The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/usb/ums.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1998 The NetBSD Foundation, Inc.
    3  * All rights reserved.
    4  *
    5  * This code is derived from software contributed to The NetBSD Foundation
    6  * by Lennart Augustsson (lennart@augustsson.net) at
    7  * Carlstedt Research & Technology.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  * 3. All advertising materials mentioning features or use of this software
   18  *    must display the following acknowledgement:
   19  *        This product includes software developed by the NetBSD
   20  *        Foundation, Inc. and its contributors.
   21  * 4. Neither the name of The NetBSD Foundation nor the names of its
   22  *    contributors may be used to endorse or promote products derived
   23  *    from this software without specific prior written permission.
   24  *
   25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35  * POSSIBILITY OF SUCH DAMAGE.
   36  */
   37 
   38 #include <sys/cdefs.h>
   39 __FBSDID("$FreeBSD: releng/7.3/sys/dev/usb/ums.c 182073 2008-08-23 17:35:15Z kaiw $");
   40 
   41 /*
   42  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
   43  */
   44 
   45 #include <sys/param.h>
   46 #include <sys/systm.h>
   47 #include <sys/kernel.h>
   48 #include <sys/malloc.h>
   49 #include <sys/module.h>
   50 #include <sys/bus.h>
   51 #include <sys/ioccom.h>
   52 #include <sys/conf.h>
   53 #include <sys/fcntl.h>
   54 #include <sys/tty.h>
   55 #include <sys/file.h>
   56 #include <sys/selinfo.h>
   57 #include <sys/poll.h>
   58 #include <sys/sysctl.h>
   59 #include <sys/uio.h>
   60 
   61 #include <dev/usb/usb.h>
   62 #include <dev/usb/usbhid.h>
   63 
   64 #include <dev/usb/usbdi.h>
   65 #include <dev/usb/usbdi_util.h>
   66 #include "usbdevs.h"
   67 #include <dev/usb/usb_quirks.h>
   68 #include <dev/usb/hid.h>
   69 
   70 #include <sys/mouse.h>
   71 
   72 #ifdef USB_DEBUG
   73 #define DPRINTF(x)      if (umsdebug) printf x
   74 #define DPRINTFN(n,x)   if (umsdebug>(n)) printf x
   75 int     umsdebug = 0;
   76 SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
   77 SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
   78            &umsdebug, 0, "ums debug level");
   79 #else
   80 #define DPRINTF(x)
   81 #define DPRINTFN(n,x)
   82 #endif
   83 
   84 #define UMSUNIT(s)      (minor(s)&0x1f)
   85 
   86 #define MS_TO_TICKS(ms) ((ms) * hz / 1000)
   87 
   88 #define QUEUE_BUFSIZE   400     /* MUST be divisible by 5 _and_ 8 */
   89 
   90 struct ums_softc {
   91         device_t sc_dev;                /* base device */
   92         usbd_interface_handle sc_iface; /* interface */
   93         usbd_pipe_handle sc_intrpipe;   /* interrupt pipe */
   94         int sc_ep_addr;
   95 
   96         u_char *sc_ibuf;
   97         u_int8_t sc_iid;
   98         int sc_isize;
   99         struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_t, sc_loc_w;
  100         struct hid_location *sc_loc_btn;
  101 
  102         struct callout callout_handle;  /* for spurious button ups */
  103 
  104         int sc_enabled;
  105         int sc_disconnected;    /* device is gone */
  106 
  107         int flags;              /* device configuration */
  108 #define UMS_Z           0x01    /* z direction available */
  109 #define UMS_SPUR_BUT_UP 0x02    /* spurious button up events */
  110 #define UMS_T           0x04    /* aa direction available (tilt) */
  111 #define UMS_REVZ        0x08    /* Z-axis is reversed */
  112         int nbuttons;
  113 #define MAX_BUTTONS     31      /* chosen because sc_buttons is int */
  114 
  115         u_char          qbuf[QUEUE_BUFSIZE];    /* must be divisable by 3&4 */
  116         u_char          dummy[100];     /* XXX just for safety and for now */
  117         int             qcount, qhead, qtail;
  118         mousehw_t       hw;
  119         mousemode_t     mode;
  120         mousestatus_t   status;
  121 
  122         int             state;
  123 #         define        UMS_ASLEEP      0x01    /* readFromDevice is waiting */
  124 #         define        UMS_SELECT      0x02    /* select is waiting */
  125         struct selinfo  rsel;           /* process waiting in select */
  126 
  127         struct cdev *dev;               /* specfs */
  128 };
  129 
  130 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
  131 #define MOUSE_FLAGS (HIO_RELATIVE)
  132 
  133 static void ums_intr(usbd_xfer_handle xfer,
  134                           usbd_private_handle priv, usbd_status status);
  135 
  136 static void ums_add_to_queue(struct ums_softc *sc,
  137                                 int dx, int dy, int dz, int dt, int buttons);
  138 static void ums_add_to_queue_timeout(void *priv);
  139 
  140 static int  ums_enable(void *);
  141 static void ums_disable(void *);
  142 
  143 static d_open_t  ums_open;
  144 static d_close_t ums_close;
  145 static d_read_t  ums_read;
  146 static d_ioctl_t ums_ioctl;
  147 static d_poll_t  ums_poll;
  148 
  149 
  150 static struct cdevsw ums_cdevsw = {
  151         .d_version =    D_VERSION,
  152         .d_flags =      D_NEEDGIANT,
  153         .d_open =       ums_open,
  154         .d_close =      ums_close,
  155         .d_read =       ums_read,
  156         .d_ioctl =      ums_ioctl,
  157         .d_poll =       ums_poll,
  158         .d_name =       "ums",
  159 };
  160 
  161 static device_probe_t ums_match;
  162 static device_attach_t ums_attach;
  163 static device_detach_t ums_detach;
  164 
  165 static device_method_t ums_methods[] = {
  166         /* Device interface */
  167         DEVMETHOD(device_probe,         ums_match),
  168         DEVMETHOD(device_attach,        ums_attach),
  169         DEVMETHOD(device_detach,        ums_detach),
  170 
  171         { 0, 0 }
  172 };
  173 
  174 static driver_t ums_driver = {
  175         "ums",
  176         ums_methods,
  177         sizeof(struct ums_softc)
  178 };
  179 
  180 static devclass_t ums_devclass;
  181 
  182 static int
  183 ums_match(device_t self)
  184 {
  185         struct usb_attach_arg *uaa = device_get_ivars(self);
  186         usb_interface_descriptor_t *id;
  187         int size, ret;
  188         void *desc;
  189         usbd_status err;
  190 
  191         if (!uaa->iface)
  192                 return (UMATCH_NONE);
  193         id = usbd_get_interface_descriptor(uaa->iface);
  194         if (!id || id->bInterfaceClass != UICLASS_HID)
  195                 return (UMATCH_NONE);
  196 
  197         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
  198         if (err)
  199                 return (UMATCH_NONE);
  200 
  201         if (hid_is_collection(desc, size,
  202                               HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
  203                 ret = UMATCH_IFACECLASS;
  204         else if (id->bInterfaceClass == UICLASS_HID &&
  205             id->bInterfaceSubClass == UISUBCLASS_BOOT &&
  206             id->bInterfaceProtocol == UIPROTO_MOUSE)
  207                 ret = UMATCH_IFACECLASS;
  208         else
  209                 ret = UMATCH_NONE;
  210 
  211         free(desc, M_TEMP);
  212         return (ret);
  213 }
  214 
  215 static int
  216 ums_attach(device_t self)
  217 {
  218         struct ums_softc *sc = device_get_softc(self);
  219         struct usb_attach_arg *uaa = device_get_ivars(self);
  220         usbd_interface_handle iface = uaa->iface;
  221         usb_interface_descriptor_t *id;
  222         usb_endpoint_descriptor_t *ed;
  223         int size;
  224         void *desc;
  225         usbd_status err;
  226         u_int32_t flags;
  227         int i, wheel;
  228         struct hid_location loc_btn;
  229 
  230         sc->sc_disconnected = 1;
  231         sc->sc_iface = iface;
  232         id = usbd_get_interface_descriptor(iface);
  233         sc->sc_dev = self;
  234         ed = usbd_interface2endpoint_descriptor(iface, 0);
  235         if (!ed) {
  236                 printf("%s: could not read endpoint descriptor\n",
  237                        device_get_nameunit(sc->sc_dev));
  238                 return ENXIO;
  239         }
  240 
  241         DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
  242                      "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
  243                      " bInterval=%d\n",
  244                      ed->bLength, ed->bDescriptorType,
  245                      UE_GET_ADDR(ed->bEndpointAddress),
  246                      UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
  247                      UE_GET_XFERTYPE(ed->bmAttributes),
  248                      UGETW(ed->wMaxPacketSize), ed->bInterval));
  249 
  250         if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
  251             UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
  252                 printf("%s: unexpected endpoint\n",
  253                        device_get_nameunit(sc->sc_dev));
  254                 return ENXIO;
  255         }
  256 
  257         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
  258         if (err)
  259                 return ENXIO;
  260 
  261         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
  262                        hid_input, &sc->sc_loc_x, &flags)) {
  263                 printf("%s: mouse has no X report\n", device_get_nameunit(sc->sc_dev));
  264                 return ENXIO;
  265         }
  266         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  267                 printf("%s: X report 0x%04x not supported\n",
  268                        device_get_nameunit(sc->sc_dev), flags);
  269                 return ENXIO;
  270         }
  271 
  272         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
  273                        hid_input, &sc->sc_loc_y, &flags)) {
  274                 printf("%s: mouse has no Y report\n", device_get_nameunit(sc->sc_dev));
  275                 return ENXIO;
  276         }
  277         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  278                 printf("%s: Y report 0x%04x not supported\n",
  279                        device_get_nameunit(sc->sc_dev), flags);
  280                 return ENXIO;
  281         }
  282 
  283         /* Try the wheel first as the Z activator since it's tradition. */
  284         wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
  285                                                   HUG_WHEEL),
  286                             hid_input, &sc->sc_loc_z, &flags) ||
  287                 hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
  288                                                   HUG_TWHEEL),
  289                             hid_input, &sc->sc_loc_z, &flags);
  290 
  291         if (wheel) {
  292                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  293                         printf("\n%s: Wheel report 0x%04x not supported\n",
  294                                device_get_nameunit(sc->sc_dev), flags);
  295                         sc->sc_loc_z.size = 0;  /* Bad Z coord, ignore it */
  296                 } else {
  297                         sc->flags |= UMS_Z;
  298                         if (usbd_get_quirks(uaa->device)->uq_flags &
  299                             UQ_MS_REVZ) {
  300                                 /* Some wheels need the Z axis reversed. */
  301                                 sc->flags |= UMS_REVZ;
  302                         }
  303 
  304                 }
  305                 /*
  306                  * We might have both a wheel and Z direction, if so put
  307                  * put the Z on the W coordinate.
  308                  */
  309                 if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
  310                                                       HUG_Z),
  311                                 hid_input, &sc->sc_loc_w, &flags)) {
  312                         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  313                                 printf("\n%s: Z report 0x%04x not supported\n",
  314                                        device_get_nameunit(sc->sc_dev), flags);
  315                                 sc->sc_loc_w.size = 0;  /* Bad Z, ignore */
  316                         }
  317                 }
  318         } else if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
  319                                                      HUG_Z),
  320                                hid_input, &sc->sc_loc_z, &flags)) {
  321                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  322                         printf("\n%s: Z report 0x%04x not supported\n",
  323                                device_get_nameunit(sc->sc_dev), flags);
  324                         sc->sc_loc_z.size = 0;  /* Bad Z coord, ignore it */
  325                 } else {
  326                         sc->flags |= UMS_Z;
  327                 }
  328         }
  329 
  330         /*
  331          * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
  332          * using 0x0048 (i've called it HUG_TWHEEL) and seems to expect
  333          * you to know that the byte after the wheel is the tilt axis.
  334          * There are no other HID axis descriptors other than X,Y and 
  335          * TWHEEL
  336          */
  337         if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
  338                         hid_input, &sc->sc_loc_t, &flags)) {
  339                         sc->sc_loc_t.pos = sc->sc_loc_t.pos + 8;
  340                         sc->flags |= UMS_T;
  341         }
  342 
  343         /* figure out the number of buttons */
  344         for (i = 1; i <= MAX_BUTTONS; i++)
  345                 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
  346                                 hid_input, &loc_btn, 0))
  347                         break;
  348         sc->nbuttons = i - 1;
  349         sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons,
  350                                 M_USBDEV, M_NOWAIT);
  351         if (!sc->sc_loc_btn) {
  352                 printf("%s: no memory\n", device_get_nameunit(sc->sc_dev));
  353                 return ENXIO;
  354         }
  355 
  356         printf("%s: %d buttons%s%s.\n", device_get_nameunit(sc->sc_dev),
  357                sc->nbuttons, sc->flags & UMS_Z? " and Z dir" : "", 
  358                sc->flags & UMS_T?" and a TILT dir": "");
  359 
  360         for (i = 1; i <= sc->nbuttons; i++)
  361                 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
  362                                 hid_input, &sc->sc_loc_btn[i-1], 0);
  363 
  364         sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
  365 
  366         /*
  367          * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
  368          * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
  369          * all of its other button positions are all off. It also reports that
  370          * it has two addional buttons and a tilt wheel.
  371          */
  372         if (usbd_get_quirks(uaa->device)->uq_flags & UQ_MS_BAD_CLASS) {
  373                 sc->flags = UMS_Z;
  374                 sc->flags |= UMS_SPUR_BUT_UP;
  375                 sc->nbuttons = 3;
  376                 sc->sc_isize = 5;
  377                 sc->sc_iid = 0;
  378                 /* 1st byte of descriptor report contains garbage */
  379                 sc->sc_loc_x.pos = 16;
  380                 sc->sc_loc_y.pos = 24;
  381                 sc->sc_loc_z.pos = 32;
  382                 sc->sc_loc_btn[0].pos = 8;
  383                 sc->sc_loc_btn[1].pos = 9;
  384                 sc->sc_loc_btn[2].pos = 10;
  385         }
  386 
  387         /*
  388          * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has
  389          * five Report IDs: 19 23 24 17 18 (in the order they appear in report
  390          * descriptor), it seems that report id 17 contains the necessary
  391          * mouse information(3-buttons,X,Y,wheel) so we specify it manually.
  392          */
  393         if (uaa->vendor == USB_VENDOR_MICROSOFT &&
  394             uaa->product == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3) {
  395                 sc->flags = UMS_Z;
  396                 sc->nbuttons = 3;
  397                 sc->sc_isize = 5;
  398                 sc->sc_iid = 17;
  399                 sc->sc_loc_x.pos = 8;
  400                 sc->sc_loc_y.pos = 16;
  401                 sc->sc_loc_z.pos = 24;
  402                 sc->sc_loc_btn[0].pos = 0;
  403                 sc->sc_loc_btn[1].pos = 1;
  404                 sc->sc_loc_btn[2].pos = 2;
  405         }
  406 
  407         sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT);
  408         if (!sc->sc_ibuf) {
  409                 printf("%s: no memory\n", device_get_nameunit(sc->sc_dev));
  410                 free(sc->sc_loc_btn, M_USB);
  411                 return ENXIO;
  412         }
  413 
  414         sc->sc_ep_addr = ed->bEndpointAddress;
  415         sc->sc_disconnected = 0;
  416         free(desc, M_TEMP);
  417 
  418 #ifdef USB_DEBUG
  419         DPRINTF(("ums_attach: sc=%p\n", sc));
  420         DPRINTF(("ums_attach: X\t%d/%d\n",
  421                  sc->sc_loc_x.pos, sc->sc_loc_x.size));
  422         DPRINTF(("ums_attach: Y\t%d/%d\n",
  423                  sc->sc_loc_y.pos, sc->sc_loc_y.size));
  424         if (sc->flags & UMS_Z)
  425                 DPRINTF(("ums_attach: Z\t%d/%d\n",
  426                          sc->sc_loc_z.pos, sc->sc_loc_z.size));
  427         for (i = 1; i <= sc->nbuttons; i++) {
  428                 DPRINTF(("ums_attach: B%d\t%d/%d\n",
  429                          i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
  430         }
  431         DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
  432 #endif
  433 
  434         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
  435                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
  436         else
  437                 sc->hw.buttons = sc->nbuttons;
  438         sc->hw.iftype = MOUSE_IF_USB;
  439         sc->hw.type = MOUSE_MOUSE;
  440         sc->hw.model = MOUSE_MODEL_GENERIC;
  441         sc->hw.hwid = 0;
  442         sc->mode.protocol = MOUSE_PROTO_MSC;
  443         sc->mode.rate = -1;
  444         sc->mode.resolution = MOUSE_RES_UNKNOWN;
  445         sc->mode.accelfactor = 0;
  446         sc->mode.level = 0;
  447         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
  448         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
  449         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
  450 
  451         sc->status.flags = 0;
  452         sc->status.button = sc->status.obutton = 0;
  453         sc->status.dx = sc->status.dy = sc->status.dz = 0;
  454 
  455         sc->dev = make_dev(&ums_cdevsw, device_get_unit(self),
  456                         UID_ROOT, GID_OPERATOR,
  457                         0644, "ums%d", device_get_unit(self));
  458 
  459         callout_init(&sc->callout_handle, 0);
  460         if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
  461                 DPRINTF(("%s: Spurious button up events\n",
  462                         device_get_nameunit(sc->sc_dev)));
  463                 sc->flags |= UMS_SPUR_BUT_UP;
  464         }
  465 
  466         return 0;
  467 }
  468 
  469 
  470 static int
  471 ums_detach(device_t self)
  472 {
  473         struct ums_softc *sc = device_get_softc(self);
  474 
  475         if (sc->sc_enabled)
  476                 ums_disable(sc);
  477 
  478         DPRINTF(("%s: disconnected\n", device_get_nameunit(self)));
  479 
  480         free(sc->sc_loc_btn, M_USB);
  481         free(sc->sc_ibuf, M_USB);
  482 
  483         /* someone waiting for data */
  484         /*
  485          * XXX If we wakeup the process here, the device will be gone by
  486          * the time the process gets a chance to notice. *_close and friends
  487          * should be fixed to handle this case.
  488          * Or we should do a delayed detach for this.
  489          * Does this delay now force tsleep to exit with an error?
  490          */
  491         if (sc->state & UMS_ASLEEP) {
  492                 sc->state &= ~UMS_ASLEEP;
  493                 wakeup(sc);
  494         }
  495         if (sc->state & UMS_SELECT) {
  496                 sc->state &= ~UMS_SELECT;
  497                 selwakeuppri(&sc->rsel, PZERO);
  498         }
  499 
  500         destroy_dev(sc->dev);
  501 
  502         return 0;
  503 }
  504 
  505 void
  506 ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
  507 {
  508         struct ums_softc *sc = addr;
  509         u_char *ibuf;
  510         int dx, dy, dz, dw, dt;
  511         int buttons = 0;
  512         int i;
  513 
  514 #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
  515 
  516         DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
  517         DPRINTFN(5, ("ums_intr: data ="));
  518         for (i = 0; i < sc->sc_isize; i++)
  519                 DPRINTFN(5, (" %02x", sc->sc_ibuf[i]));
  520         DPRINTFN(5, ("\n"));
  521 
  522         if (status == USBD_CANCELLED)
  523                 return;
  524 
  525         if (status != USBD_NORMAL_COMPLETION) {
  526                 DPRINTF(("ums_intr: status=%d\n", status));
  527                 if (status == USBD_STALLED)
  528                     usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
  529                 if(status != USBD_IOERROR)
  530                         return;
  531         }
  532 
  533         ibuf = sc->sc_ibuf;
  534         /*
  535          * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte of
  536          * data compared to most USB mice. This byte frequently switches
  537          * from 0x01 (usual state) to 0x02. I assume it is to allow
  538          * extra, non-standard, reporting (say battery-life). However
  539          * at the same time it generates a left-click message on the button
  540          * byte which causes spurious left-click's where there shouldn't be.
  541          * This should sort that.
  542          * Currently it's the only user of UMS_T so use it as an identifier.
  543          * We probably should switch to some more official quirk.
  544          *
  545          * UPDATE: This problem affects the M$ Wireless Notebook Optical Mouse,
  546          * too. However, the leading byte for this mouse is normally 0x11,
  547          * and the phantom mouse click occurs when its 0x14.
  548          */
  549         if (sc->flags & UMS_T) {
  550                 if (sc->sc_iid) {
  551                         if (*ibuf++ == 0x02)
  552                                 return;
  553                 }
  554         } else if (sc->flags & UMS_SPUR_BUT_UP) {
  555                 DPRINTFN(5, ("ums_intr: #### ibuf[0] =3D %d ####\n", *ibuf));
  556                 if (*ibuf == 0x14 || *ibuf == 0x15)
  557                         return;
  558         } else {
  559                 if (sc->sc_iid) {
  560                         if (*ibuf++ != sc->sc_iid)
  561                                 return;
  562                 }
  563         }
  564 
  565         dx =  hid_get_data(ibuf, &sc->sc_loc_x);
  566         dy = -hid_get_data(ibuf, &sc->sc_loc_y);
  567         dz = -hid_get_data(ibuf, &sc->sc_loc_z);
  568         dw =  hid_get_data(ibuf, &sc->sc_loc_w);
  569         if (sc->flags & UMS_REVZ)
  570                 dz = -dz;
  571         if (sc->flags & UMS_T)
  572                 dt = -hid_get_data(ibuf, &sc->sc_loc_t);
  573         else
  574                 dt = 0;
  575         for (i = 0; i < sc->nbuttons; i++)
  576                 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
  577                         buttons |= (1 << UMS_BUT(i));
  578 
  579         if (dx || dy || dz || dt || dw || (sc->flags & UMS_Z)
  580             || buttons != sc->status.button) {
  581                 DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d w:%d t:%d buttons:0x%x\n",
  582                         dx, dy, dz, dw, dt, buttons));
  583 
  584                 sc->status.button = buttons;
  585                 sc->status.dx += dx;
  586                 sc->status.dy += dy;
  587                 sc->status.dz += dz;
  588                 /* sc->status.dt += dt; */ /* no way to export this yet */
  589                 /* sc->status.dw += dw; */ /* idem */
  590                 
  591                 /* Discard data in case of full buffer */
  592                 if (sc->qcount == sizeof(sc->qbuf)) {
  593                         DPRINTF(("Buffer full, discarded packet"));
  594                         return;
  595                 }
  596 
  597                 /*
  598                  * The Qtronix keyboard has a built in PS/2 port for a mouse.
  599                  * The firmware once in a while posts a spurious button up
  600                  * event. This event we ignore by doing a timeout for 50 msecs.
  601                  * If we receive dx=dy=dz=buttons=0 before we add the event to
  602                  * the queue.
  603                  * In any other case we delete the timeout event.
  604                  */
  605                 if (sc->flags & UMS_SPUR_BUT_UP &&
  606                     dx == 0 && dy == 0 && dz == 0 && dt == 0 && buttons == 0) {
  607                         callout_reset(&sc->callout_handle, MS_TO_TICKS(50),
  608                             ums_add_to_queue_timeout, (void *) sc);
  609                 } else {
  610                         callout_stop(&sc->callout_handle);
  611                         ums_add_to_queue(sc, dx, dy, dz, dt, buttons);
  612                 }
  613         }
  614 }
  615 
  616 static void
  617 ums_add_to_queue_timeout(void *priv)
  618 {
  619         struct ums_softc *sc = priv;
  620         int s;
  621 
  622         s = splusb();
  623         ums_add_to_queue(sc, 0, 0, 0, 0, 0);
  624         splx(s);
  625 }
  626 
  627 static void
  628 ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int dt, int buttons)
  629 {
  630         /* Discard data in case of full buffer */
  631         if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
  632                 DPRINTF(("Buffer full, discarded packet"));
  633                 return;
  634         }
  635 
  636         if (dx >  254)          dx =  254;
  637         if (dx < -256)          dx = -256;
  638         if (dy >  254)          dy =  254;
  639         if (dy < -256)          dy = -256;
  640         if (dz >  126)          dz =  126;
  641         if (dz < -128)          dz = -128;
  642         if (dt >  126)          dt =  126;
  643         if (dt < -128)          dt = -128;
  644 
  645         sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
  646         sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
  647         sc->qbuf[sc->qhead+1] = dx >> 1;
  648         sc->qbuf[sc->qhead+2] = dy >> 1;
  649         sc->qbuf[sc->qhead+3] = dx - (dx >> 1);
  650         sc->qbuf[sc->qhead+4] = dy - (dy >> 1);
  651 
  652         if (sc->mode.level == 1) {
  653                 sc->qbuf[sc->qhead+5] = dz >> 1;
  654                 sc->qbuf[sc->qhead+6] = dz - (dz >> 1);
  655                 sc->qbuf[sc->qhead+7] = ((~buttons >> 3)
  656                                          & MOUSE_SYS_EXTBUTTONS);
  657         }
  658 
  659         sc->qhead += sc->mode.packetsize;
  660         sc->qcount += sc->mode.packetsize;
  661         /* wrap round at end of buffer */
  662         if (sc->qhead >= sizeof(sc->qbuf))
  663                 sc->qhead = 0;
  664 
  665         /* someone waiting for data */
  666         if (sc->state & UMS_ASLEEP) {
  667                 sc->state &= ~UMS_ASLEEP;
  668                 wakeup(sc);
  669         }
  670         if (sc->state & UMS_SELECT) {
  671                 sc->state &= ~UMS_SELECT;
  672                 selwakeuppri(&sc->rsel, PZERO);
  673         }
  674 }
  675 static int
  676 ums_enable(v)
  677         void *v;
  678 {
  679         struct ums_softc *sc = v;
  680 
  681         usbd_status err;
  682 
  683         if (sc->sc_enabled)
  684                 return EBUSY;
  685 
  686         sc->sc_enabled = 1;
  687         sc->qcount = 0;
  688         sc->qhead = sc->qtail = 0;
  689         sc->status.flags = 0;
  690         sc->status.button = sc->status.obutton = 0;
  691         sc->status.dx = sc->status.dy = sc->status.dz /* = sc->status.dt */ = 0;
  692 
  693         callout_handle_init((struct callout_handle *)&sc->callout_handle);
  694 
  695         /*
  696          * Force the report (non-boot) protocol.
  697          *
  698          * Mice without boot protocol support may choose not to implement
  699          * Set_Protocol at all; do not check for error.
  700          */
  701         usbd_set_protocol(sc->sc_iface, 1);
  702 
  703         /* Set up interrupt pipe. */
  704         err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
  705                                 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
  706                                 sc->sc_ibuf, sc->sc_isize, ums_intr,
  707                                 USBD_DEFAULT_INTERVAL);
  708         if (err) {
  709                 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
  710                          err));
  711                 sc->sc_enabled = 0;
  712                 return (EIO);
  713         }
  714         return (0);
  715 }
  716 
  717 static void
  718 ums_disable(priv)
  719         void *priv;
  720 {
  721         struct ums_softc *sc = priv;
  722 
  723         callout_stop(&sc->callout_handle);
  724 
  725         /* Disable interrupts. */
  726         usbd_abort_pipe(sc->sc_intrpipe);
  727         usbd_close_pipe(sc->sc_intrpipe);
  728 
  729         sc->sc_enabled = 0;
  730 
  731         if (sc->qcount != 0)
  732                 DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
  733 }
  734 
  735 static int
  736 ums_open(struct cdev *dev, int flag, int fmt, struct thread *p)
  737 {
  738         struct ums_softc *sc;
  739 
  740         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  741         if (sc == NULL)
  742                 return (ENXIO);
  743 
  744         return ums_enable(sc);
  745 }
  746 
  747 static int
  748 ums_close(struct cdev *dev, int flag, int fmt, struct thread *p)
  749 {
  750         struct ums_softc *sc;
  751 
  752         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  753         if (!sc)
  754                 return 0;
  755 
  756         if (sc->sc_enabled)
  757                 ums_disable(sc);
  758 
  759         return 0;
  760 }
  761 
  762 static int
  763 ums_read(struct cdev *dev, struct uio *uio, int flag)
  764 {
  765         struct ums_softc *sc;
  766         int s;
  767         char buf[sizeof(sc->qbuf)];
  768         int l = 0;
  769         int error;
  770 
  771         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  772         s = splusb();
  773         if (!sc) {
  774                 splx(s);
  775                 return EIO;
  776         }
  777 
  778         while (sc->qcount == 0 )  {
  779                 if (flag & O_NONBLOCK) {                /* non-blocking I/O */
  780                         splx(s);
  781                         return EWOULDBLOCK;
  782                 }
  783 
  784                 sc->state |= UMS_ASLEEP;        /* blocking I/O */
  785                 error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
  786                 if (error) {
  787                         splx(s);
  788                         return error;
  789                 } else if (!sc->sc_enabled) {
  790                         splx(s);
  791                         return EINTR;
  792                 }
  793                 /* check whether the device is still there */
  794 
  795                 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  796                 if (!sc) {
  797                         splx(s);
  798                         return EIO;
  799                 }
  800         }
  801 
  802         /*
  803          * XXX we could optimise the use of splx/splusb somewhat. The writer
  804          * process only extends qcount and qtail. We could copy them and use the copies
  805          * to do the copying out of the queue.
  806          */
  807 
  808         while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
  809                 l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
  810                 if (l > sizeof(buf))
  811                         l = sizeof(buf);
  812                 if (l > sizeof(sc->qbuf) - sc->qtail)           /* transfer till end of buf */
  813                         l = sizeof(sc->qbuf) - sc->qtail;
  814 
  815                 splx(s);
  816                 uiomove(&sc->qbuf[sc->qtail], l, uio);
  817                 s = splusb();
  818 
  819                 if ( sc->qcount - l < 0 ) {
  820                         DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
  821                         sc->qcount = l;
  822                 }
  823                 sc->qcount -= l;        /* remove the bytes from the buffer */
  824                 sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
  825         }
  826         splx(s);
  827 
  828         return 0;
  829 }
  830 
  831 static int
  832 ums_poll(struct cdev *dev, int events, struct thread *p)
  833 {
  834         struct ums_softc *sc;
  835         int revents = 0;
  836         int s;
  837 
  838         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  839         if (!sc)
  840                 return 0;
  841 
  842         s = splusb();
  843         if (events & (POLLIN | POLLRDNORM)) {
  844                 if (sc->qcount) {
  845                         revents = events & (POLLIN | POLLRDNORM);
  846                 } else {
  847                         sc->state |= UMS_SELECT;
  848                         selrecord(p, &sc->rsel);
  849                 }
  850         }
  851         splx(s);
  852 
  853         return revents;
  854 }
  855 
  856 int
  857 ums_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *p)
  858 {
  859         struct ums_softc *sc;
  860         int error = 0;
  861         int s;
  862         mousemode_t mode;
  863 
  864         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  865         if (!sc)
  866                 return EIO;
  867 
  868         switch(cmd) {
  869         case MOUSE_GETHWINFO:
  870                 *(mousehw_t *)addr = sc->hw;
  871                 break;
  872         case MOUSE_GETMODE:
  873                 *(mousemode_t *)addr = sc->mode;
  874                 break;
  875         case MOUSE_SETMODE:
  876                 mode = *(mousemode_t *)addr;
  877 
  878                 if (mode.level == -1)
  879                         /* don't change the current setting */
  880                         ;
  881                 else if ((mode.level < 0) || (mode.level > 1))
  882                         return (EINVAL);
  883 
  884                 s = splusb();
  885                 sc->mode.level = mode.level;
  886 
  887                 if (sc->mode.level == 0) {
  888                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
  889                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
  890                         else
  891                                 sc->hw.buttons = sc->nbuttons;
  892                         sc->mode.protocol = MOUSE_PROTO_MSC;
  893                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
  894                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
  895                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
  896                 } else if (sc->mode.level == 1) {
  897                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
  898                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
  899                         else
  900                                 sc->hw.buttons = sc->nbuttons;
  901                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
  902                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
  903                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
  904                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
  905                 }
  906 
  907                 bzero(sc->qbuf, sizeof(sc->qbuf));
  908                 sc->qhead = sc->qtail = sc->qcount = 0;
  909                 splx(s);
  910 
  911                 break;
  912         case MOUSE_GETLEVEL:
  913                 *(int *)addr = sc->mode.level;
  914                 break;
  915         case MOUSE_SETLEVEL:
  916                 if (*(int *)addr < 0 || *(int *)addr > 1)
  917                         return (EINVAL);
  918 
  919                 s = splusb();
  920                 sc->mode.level = *(int *)addr;
  921 
  922                 if (sc->mode.level == 0) {
  923                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
  924                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
  925                         else
  926                                 sc->hw.buttons = sc->nbuttons;
  927                         sc->mode.protocol = MOUSE_PROTO_MSC;
  928                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
  929                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
  930                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
  931                 } else if (sc->mode.level == 1) {
  932                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
  933                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
  934                         else
  935                                 sc->hw.buttons = sc->nbuttons;
  936                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
  937                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
  938                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
  939                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
  940                 }
  941 
  942                 bzero(sc->qbuf, sizeof(sc->qbuf));
  943                 sc->qhead = sc->qtail = sc->qcount = 0;
  944                 splx(s);
  945 
  946                 break;
  947         case MOUSE_GETSTATUS: {
  948                 mousestatus_t *status = (mousestatus_t *) addr;
  949 
  950                 s = splusb();
  951                 *status = sc->status;
  952                 sc->status.obutton = sc->status.button;
  953                 sc->status.button = 0;
  954                 sc->status.dx = sc->status.dy
  955                     = sc->status.dz = /* sc->status.dt = */ 0;
  956                 splx(s);
  957 
  958                 if (status->dx || status->dy || status->dz /* || status->dt */)
  959                         status->flags |= MOUSE_POSCHANGED;
  960                 if (status->button != status->obutton)
  961                         status->flags |= MOUSE_BUTTONSCHANGED;
  962                 break;
  963                 }
  964         default:
  965                 error = ENOTTY;
  966         }
  967 
  968         return error;
  969 }
  970 
  971 MODULE_DEPEND(ums, usb, 1, 1, 1);
  972 DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0);

Cache object: 0cb756314881a68ec2a20181d9ae600d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.