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/usbmisc/ums/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  * $FreeBSD: src/sys/dev/usb/ums.c,v 1.64 2003/11/09 09:17:22 tanimura Exp $
    3  */
    4 
    5 /*
    6  * Copyright (c) 1998 The NetBSD Foundation, Inc.
    7  * All rights reserved.
    8  *
    9  * This code is derived from software contributed to The NetBSD Foundation
   10  * by Lennart Augustsson (lennart@augustsson.net) at
   11  * Carlstedt Research & Technology.
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  * 1. Redistributions of source code must retain the above copyright
   17  *    notice, this list of conditions and the following disclaimer.
   18  * 2. Redistributions in binary form must reproduce the above copyright
   19  *    notice, this list of conditions and the following disclaimer in the
   20  *    documentation and/or other materials provided with the distribution.
   21  * 3. All advertising materials mentioning features or use of this software
   22  *    must display the following acknowledgement:
   23  *        This product includes software developed by the NetBSD
   24  *        Foundation, Inc. and its contributors.
   25  * 4. Neither the name of The NetBSD Foundation nor the names of its
   26  *    contributors may be used to endorse or promote products derived
   27  *    from this software without specific prior written permission.
   28  *
   29  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   32  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   39  * POSSIBILITY OF SUCH DAMAGE.
   40  */
   41 
   42 /*
   43  * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf
   44  */
   45 
   46 #include <sys/param.h>
   47 #include <sys/systm.h>
   48 #include <sys/kernel.h>
   49 #include <sys/malloc.h>
   50 #include <sys/module.h>
   51 #include <sys/bus.h>
   52 #include <sys/conf.h>
   53 #include <sys/tty.h>
   54 #include <sys/file.h>
   55 #include <sys/vnode.h>
   56 #include <sys/event.h>
   57 #include <sys/sysctl.h>
   58 #include <sys/devfs.h>
   59 #include <sys/thread2.h>
   60 
   61 #include <bus/usb/usb.h>
   62 #include <bus/usb/usbhid.h>
   63 
   64 #include <bus/usb/usbdi.h>
   65 #include <bus/usb/usbdi_util.h>
   66 #include <bus/usb/usb_quirks.h>
   67 #include <bus/usb/hid.h>
   68 
   69 #include <sys/mouse.h>
   70 
   71 #ifdef USB_DEBUG
   72 #define DPRINTF(x)      if (umsdebug) kprintf x
   73 #define DPRINTFN(n,x)   if (umsdebug>(n)) kprintf x
   74 int     umsdebug = 0;
   75 SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
   76 SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
   77            &umsdebug, 0, "ums debug level");
   78 #else
   79 #define DPRINTF(x)
   80 #define DPRINTFN(n,x)
   81 #endif
   82 
   83 #define UMSUNIT(s)      (minor(s)&0x1f)
   84 
   85 #define MS_TO_TICKS(ms) ((ms) * hz / 1000)
   86 
   87 #define QUEUE_BUFSIZE   400     /* MUST be divisible by 5 _and_ 8 */
   88 
   89 struct ums_softc {
   90         device_t sc_dev;                /* base device */
   91         cdev_t   sc_cdev;
   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;
  100         struct hid_location *sc_loc_btn;
  101 
  102         struct callout sc_timeout;      /* 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         int nbuttons;
  111 #define MAX_BUTTONS     31      /* must not exceed size of sc_buttons */
  112 
  113         u_char          qbuf[QUEUE_BUFSIZE];    /* must be divisable by 3&4 */
  114         u_char          dummy[100];     /* XXX just for safety and for now */
  115         int             qcount, qhead, qtail;
  116         mousehw_t       hw;
  117         mousemode_t     mode;
  118         mousestatus_t   status;
  119 
  120         int             state;
  121 #         define        UMS_ASLEEP      0x01    /* readFromDevice is waiting */
  122         struct kqinfo   rkq;            /* process waiting in select/poll/kq */
  123 };
  124 
  125 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
  126 #define MOUSE_FLAGS (HIO_RELATIVE)
  127 
  128 static void ums_intr(usbd_xfer_handle xfer,
  129                           usbd_private_handle priv, usbd_status status);
  130 
  131 static void ums_add_to_queue(struct ums_softc *sc,
  132                                 int dx, int dy, int dz, int buttons);
  133 static void ums_add_to_queue_timeout(void *priv);
  134 
  135 static int  ums_enable(void *);
  136 static void ums_disable(void *);
  137 
  138 static d_open_t  ums_open;
  139 static d_close_t ums_close;
  140 static d_read_t  ums_read;
  141 static d_ioctl_t ums_ioctl;
  142 static d_kqfilter_t ums_kqfilter;
  143 
  144 static void ums_filt_detach(struct knote *);
  145 static int ums_filt(struct knote *, long);
  146 
  147 static struct dev_ops ums_ops = {
  148         { "ums", 0, 0 },
  149         .d_open =       ums_open,
  150         .d_close =      ums_close,
  151         .d_read =       ums_read,
  152         .d_ioctl =      ums_ioctl,
  153         .d_kqfilter =   ums_kqfilter
  154 };
  155 
  156 static device_probe_t ums_match;
  157 static device_attach_t ums_attach;
  158 static device_detach_t ums_detach;
  159 
  160 static devclass_t ums_devclass;
  161 
  162 static kobj_method_t ums_methods[] = {
  163         DEVMETHOD(device_probe, ums_match),
  164         DEVMETHOD(device_attach, ums_attach),
  165         DEVMETHOD(device_detach, ums_detach),
  166         DEVMETHOD_END
  167 };
  168 
  169 static driver_t ums_driver = {
  170         "ums",
  171         ums_methods,
  172         sizeof(struct ums_softc)
  173 };
  174 
  175 MODULE_DEPEND(ums, usb, 1, 1, 1);
  176 
  177 static int
  178 ums_match(device_t self)
  179 {
  180         struct usb_attach_arg *uaa = device_get_ivars(self);
  181         usb_interface_descriptor_t *id;
  182         int size, ret;
  183         void *desc;
  184         usbd_status err;
  185 
  186         if (!uaa->iface)
  187                 return (UMATCH_NONE);
  188         id = usbd_get_interface_descriptor(uaa->iface);
  189         if (!id || id->bInterfaceClass != UICLASS_HID)
  190                 return (UMATCH_NONE);
  191 
  192         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
  193         if (err)
  194                 return (UMATCH_NONE);
  195 
  196         if (hid_is_collection(desc, size,
  197                               HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
  198                 ret = UMATCH_IFACECLASS;
  199         else
  200                 ret = UMATCH_NONE;
  201 
  202         kfree(desc, M_TEMP);
  203         return (ret);
  204 }
  205 
  206 static int
  207 ums_attach(device_t self)
  208 {
  209         struct ums_softc *sc = device_get_softc(self);
  210         struct usb_attach_arg *uaa = device_get_ivars(self);
  211         usbd_interface_handle iface = uaa->iface;
  212         usb_endpoint_descriptor_t *ed;
  213         int size;
  214         void *desc;
  215         usbd_status err;
  216         u_int32_t flags;
  217         int i;
  218         struct hid_location loc_btn;
  219 
  220         sc->sc_disconnected = 1;
  221         sc->sc_iface = iface;
  222         sc->sc_dev = self;
  223         ed = usbd_interface2endpoint_descriptor(iface, 0);
  224         if (!ed) {
  225                 kprintf("%s: could not read endpoint descriptor\n",
  226                        device_get_nameunit(sc->sc_dev));
  227                 return ENXIO;
  228         }
  229 
  230         DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
  231                      "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
  232                      " bInterval=%d\n",
  233                      ed->bLength, ed->bDescriptorType,
  234                      UE_GET_ADDR(ed->bEndpointAddress),
  235                      UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
  236                      UE_GET_XFERTYPE(ed->bmAttributes),
  237                      UGETW(ed->wMaxPacketSize), ed->bInterval));
  238 
  239         if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
  240             UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
  241                 kprintf("%s: unexpected endpoint\n",
  242                        device_get_nameunit(sc->sc_dev));
  243                 return ENXIO;
  244         }
  245 
  246         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
  247         if (err)
  248                 return ENXIO;
  249 
  250         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
  251                        hid_input, &sc->sc_loc_x, &flags)) {
  252                 kprintf("%s: mouse has no X report\n", device_get_nameunit(sc->sc_dev));
  253                 return ENXIO;
  254         }
  255         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  256                 kprintf("%s: X report 0x%04x not supported\n",
  257                        device_get_nameunit(sc->sc_dev), flags);
  258                 return ENXIO;
  259         }
  260 
  261         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
  262                        hid_input, &sc->sc_loc_y, &flags)) {
  263                 kprintf("%s: mouse has no Y report\n", device_get_nameunit(sc->sc_dev));
  264                 return ENXIO;
  265         }
  266         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  267                 kprintf("%s: Y report 0x%04x not supported\n",
  268                        device_get_nameunit(sc->sc_dev), flags);
  269                 return ENXIO;
  270         }
  271 
  272         /* try to guess the Z activator: first check Z, then WHEEL */
  273         if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
  274                        hid_input, &sc->sc_loc_z, &flags) ||
  275             hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
  276                        hid_input, &sc->sc_loc_z, &flags)) {
  277                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  278                         sc->sc_loc_z.size = 0;  /* Bad Z coord, ignore it */
  279                 } else {
  280                         sc->flags |= UMS_Z;
  281                 }
  282         }
  283 
  284         /* figure out the number of buttons */
  285         for (i = 1; i <= MAX_BUTTONS; i++)
  286                 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
  287                                 hid_input, &loc_btn, 0))
  288                         break;
  289         sc->nbuttons = i - 1;
  290         sc->sc_loc_btn = kmalloc(sizeof(struct hid_location)*sc->nbuttons,
  291                                 M_USBDEV, M_INTWAIT);
  292 
  293         kprintf("%s: %d buttons%s\n", device_get_nameunit(sc->sc_dev),
  294                sc->nbuttons, sc->flags & UMS_Z? " and Z dir." : "");
  295 
  296         for (i = 1; i <= sc->nbuttons; i++)
  297                 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
  298                                 hid_input, &sc->sc_loc_btn[i-1], 0);
  299 
  300         sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
  301         sc->sc_ibuf = kmalloc(sc->sc_isize, M_USB, M_INTWAIT);
  302         sc->sc_ep_addr = ed->bEndpointAddress;
  303         sc->sc_disconnected = 0;
  304         kfree(desc, M_TEMP);
  305 
  306 #ifdef USB_DEBUG
  307         DPRINTF(("ums_attach: sc=%p\n", sc));
  308         DPRINTF(("ums_attach: X\t%d/%d\n",
  309                  sc->sc_loc_x.pos, sc->sc_loc_x.size));
  310         DPRINTF(("ums_attach: Y\t%d/%d\n",
  311                  sc->sc_loc_y.pos, sc->sc_loc_y.size));
  312         if (sc->flags & UMS_Z)
  313                 DPRINTF(("ums_attach: Z\t%d/%d\n",
  314                          sc->sc_loc_z.pos, sc->sc_loc_z.size));
  315         for (i = 1; i <= sc->nbuttons; i++) {
  316                 DPRINTF(("ums_attach: B%d\t%d/%d\n",
  317                          i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
  318         }
  319         DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
  320 #endif
  321 
  322         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
  323                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
  324         else
  325                 sc->hw.buttons = sc->nbuttons;
  326         sc->hw.iftype = MOUSE_IF_USB;
  327         sc->hw.type = MOUSE_MOUSE;
  328         sc->hw.model = MOUSE_MODEL_GENERIC;
  329         sc->hw.hwid = 0;
  330         sc->mode.protocol = MOUSE_PROTO_MSC;
  331         sc->mode.rate = -1;
  332         sc->mode.resolution = MOUSE_RES_UNKNOWN;
  333         sc->mode.accelfactor = 0;
  334         sc->mode.level = 0;
  335         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
  336         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
  337         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
  338 
  339         sc->status.flags = 0;
  340         sc->status.button = sc->status.obutton = 0;
  341         sc->status.dx = sc->status.dy = sc->status.dz = 0;
  342 
  343         sc->sc_cdev = make_dev(&ums_ops, device_get_unit(self),
  344                                UID_ROOT, GID_OPERATOR,
  345                                0644, "ums%d", device_get_unit(self));
  346         reference_dev(sc->sc_cdev);
  347 
  348         if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
  349                 DPRINTF(("%s: Spurious button up events\n",
  350                         device_get_nameunit(sc->sc_dev)));
  351                 sc->flags |= UMS_SPUR_BUT_UP;
  352         }
  353 
  354         return 0;
  355 }
  356 
  357 
  358 static int
  359 ums_detach(device_t self)
  360 {
  361         struct ums_softc *sc = device_get_softc(self);
  362 
  363         if (sc->sc_enabled)
  364                 ums_disable(sc);
  365 
  366         DPRINTF(("%s: disconnected\n", device_get_nameunit(self)));
  367 
  368         kfree(sc->sc_loc_btn, M_USB);
  369         kfree(sc->sc_ibuf, M_USB);
  370 
  371         /* someone waiting for data */
  372         /*
  373          * XXX If we wakeup the process here, the device will be gone by
  374          * the time the process gets a chance to notice. *_close and friends
  375          * should be fixed to handle this case.
  376          * Or we should do a delayed detach for this.
  377          * Does this delay now force tsleep to exit with an error?
  378          */
  379         if (sc->state & UMS_ASLEEP) {
  380                 sc->state &= ~UMS_ASLEEP;
  381                 wakeup(sc);
  382         }
  383 
  384         dev_ops_remove_minor(&ums_ops, /*-1, */device_get_unit(self));
  385         devfs_assume_knotes(sc->sc_cdev, &sc->rkq);
  386         release_dev(sc->sc_cdev);
  387         sc->sc_cdev = NULL;
  388 
  389         return 0;
  390 }
  391 
  392 void
  393 ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr,
  394          usbd_status status)
  395 {
  396         struct ums_softc *sc = addr;
  397         u_char *ibuf;
  398         int dx, dy, dz;
  399         u_char buttons = 0;
  400         int i;
  401 
  402 #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
  403 
  404         DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
  405         DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
  406                      sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
  407 
  408         if (status == USBD_CANCELLED)
  409                 return;
  410 
  411         if (status != USBD_NORMAL_COMPLETION) {
  412                 DPRINTF(("ums_intr: status=%d\n", status));
  413                 if (status == USBD_STALLED)
  414                     usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
  415                 return;
  416         }
  417 
  418         ibuf = sc->sc_ibuf;
  419         if (sc->sc_iid) {
  420                 if (*ibuf++ != sc->sc_iid)
  421                         return;
  422         }
  423 
  424         dx =  hid_get_data(ibuf, &sc->sc_loc_x);
  425         dy = -hid_get_data(ibuf, &sc->sc_loc_y);
  426         dz = -hid_get_data(ibuf, &sc->sc_loc_z);
  427         for (i = 0; i < sc->nbuttons; i++)
  428                 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
  429                         buttons |= (1 << UMS_BUT(i));
  430 
  431         if (dx || dy || dz || (sc->flags & UMS_Z)
  432             || buttons != sc->status.button) {
  433                 DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
  434                         dx, dy, dz, buttons));
  435 
  436                 sc->status.button = buttons;
  437                 sc->status.dx += dx;
  438                 sc->status.dy += dy;
  439                 sc->status.dz += dz;
  440 
  441                 /* Discard data in case of full buffer */
  442                 if (sc->qcount == sizeof(sc->qbuf)) {
  443                         DPRINTF(("Buffer full, discarded packet"));
  444                         return;
  445                 }
  446 
  447                 /*
  448                  * The Qtronix keyboard has a built in PS/2 port for a mouse.
  449                  * The firmware once in a while posts a spurious button up
  450                  * event. This event we ignore by doing a timeout for 50 msecs.
  451                  * If we receive dx=dy=dz=buttons=0 before we add the event to
  452                  * the queue.
  453                  * In any other case we delete the timeout event.
  454                  */
  455                 if (sc->flags & UMS_SPUR_BUT_UP &&
  456                     dx == 0 && dy == 0 && dz == 0 && buttons == 0) {
  457                         callout_reset(&sc->sc_timeout, MS_TO_TICKS(50),
  458                                     ums_add_to_queue_timeout, (void *) sc);
  459                 } else {
  460                         callout_stop(&sc->sc_timeout);
  461                         ums_add_to_queue(sc, dx, dy, dz, buttons);
  462                 }
  463         }
  464 }
  465 
  466 static void
  467 ums_add_to_queue_timeout(void *priv)
  468 {
  469         struct ums_softc *sc = priv;
  470 
  471         crit_enter();
  472         ums_add_to_queue(sc, 0, 0, 0, 0);
  473         crit_exit();
  474 }
  475 
  476 static void
  477 ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons)
  478 {
  479         /* Discard data in case of full buffer */
  480         if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
  481                 DPRINTF(("Buffer full, discarded packet"));
  482                 return;
  483         }
  484 
  485         if (dx >  254)          dx =  254;
  486         if (dx < -256)          dx = -256;
  487         if (dy >  254)          dy =  254;
  488         if (dy < -256)          dy = -256;
  489         if (dz >  126)          dz =  126;
  490         if (dz < -128)          dz = -128;
  491 
  492         sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
  493         sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
  494         sc->qbuf[sc->qhead+1] = dx >> 1;
  495         sc->qbuf[sc->qhead+2] = dy >> 1;
  496         sc->qbuf[sc->qhead+3] = dx - (dx >> 1);
  497         sc->qbuf[sc->qhead+4] = dy - (dy >> 1);
  498 
  499         if (sc->mode.level == 1) {
  500                 sc->qbuf[sc->qhead+5] = dz >> 1;
  501                 sc->qbuf[sc->qhead+6] = dz - (dz >> 1);
  502                 sc->qbuf[sc->qhead+7] = ((~buttons >> 3)
  503                                          & MOUSE_SYS_EXTBUTTONS);
  504         }
  505 
  506         sc->qhead += sc->mode.packetsize;
  507         sc->qcount += sc->mode.packetsize;
  508         /* wrap round at end of buffer */
  509         if (sc->qhead >= sizeof(sc->qbuf))
  510                 sc->qhead = 0;
  511 
  512         /* someone waiting for data */
  513         if (sc->state & UMS_ASLEEP) {
  514                 sc->state &= ~UMS_ASLEEP;
  515                 wakeup(sc);
  516         }
  517         KNOTE(&sc->rkq.ki_note, 0);
  518 }
  519 
  520 static int
  521 ums_enable(void *v)
  522 {
  523         struct ums_softc *sc = v;
  524 
  525         usbd_status err;
  526 
  527         if (sc->sc_enabled)
  528                 return EBUSY;
  529 
  530         sc->sc_enabled = 1;
  531         sc->qcount = 0;
  532         sc->qhead = sc->qtail = 0;
  533         sc->status.flags = 0;
  534         sc->status.button = sc->status.obutton = 0;
  535         sc->status.dx = sc->status.dy = sc->status.dz = 0;
  536 
  537         callout_init(&sc->sc_timeout);
  538 
  539         /* Set up interrupt pipe. */
  540         err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
  541                                 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
  542                                 sc->sc_ibuf, sc->sc_isize, ums_intr,
  543                                 USBD_DEFAULT_INTERVAL);
  544         if (err) {
  545                 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
  546                          err));
  547                 sc->sc_enabled = 0;
  548                 return (EIO);
  549         }
  550         return (0);
  551 }
  552 
  553 static void
  554 ums_disable(void *priv)
  555 {
  556         struct ums_softc *sc = priv;
  557 
  558         callout_stop(&sc->sc_timeout);
  559 
  560         /* Disable interrupts. */
  561         usbd_abort_pipe(sc->sc_intrpipe);
  562         usbd_close_pipe(sc->sc_intrpipe);
  563 
  564         sc->sc_enabled = 0;
  565 
  566         if (sc->qcount != 0)
  567                 DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
  568 }
  569 
  570 static int
  571 ums_open(struct dev_open_args *ap)
  572 {
  573         cdev_t dev = ap->a_head.a_dev;
  574         struct ums_softc *sc;
  575 
  576         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  577         if (sc == NULL)
  578                 return (ENXIO);
  579 
  580         return ums_enable(sc);
  581 }
  582 
  583 static int
  584 ums_close(struct dev_close_args *ap)
  585 {
  586         cdev_t dev = ap->a_head.a_dev;
  587         struct ums_softc *sc;
  588 
  589         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  590 
  591         if (!sc)
  592                 return 0;
  593 
  594         if (sc->sc_enabled)
  595                 ums_disable(sc);
  596 
  597         return 0;
  598 }
  599 
  600 static int
  601 ums_read(struct dev_read_args *ap)
  602 {
  603         cdev_t dev = ap->a_head.a_dev;
  604         struct uio *uio = ap->a_uio;
  605         struct ums_softc *sc;
  606         char buf[sizeof(sc->qbuf)];
  607         int l = 0;
  608         int error;
  609 
  610         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  611 
  612         crit_enter();
  613         if (!sc) {
  614                 crit_exit();
  615                 return EIO;
  616         }
  617 
  618         while (sc->qcount == 0 )  {
  619                 if (ap->a_ioflag & IO_NDELAY) {         /* non-blocking I/O */
  620                         crit_exit();
  621                         return EWOULDBLOCK;
  622                 }
  623 
  624                 sc->state |= UMS_ASLEEP;        /* blocking I/O */
  625                 error = tsleep(sc, PCATCH, "umsrea", 0);
  626                 if (error) {
  627                         crit_exit();
  628                         return error;
  629                 } else if (!sc->sc_enabled) {
  630                         crit_exit();
  631                         return EINTR;
  632                 }
  633                 /* check whether the device is still there */
  634 
  635                 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  636                 if (!sc) {
  637                         crit_exit();
  638                         return EIO;
  639                 }
  640         }
  641 
  642         /*
  643          * The writer process only extends qcount and qtail. We could copy
  644          * them and use the copies to do the copying out of the queue.
  645          */
  646 
  647         while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
  648                 l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
  649                 if (l > sizeof(buf))
  650                         l = sizeof(buf);
  651                 if (l > sizeof(sc->qbuf) - sc->qtail)           /* transfer till end of buf */
  652                         l = sizeof(sc->qbuf) - sc->qtail;
  653 
  654                 crit_exit();
  655                 uiomove(&sc->qbuf[sc->qtail], l, uio);
  656                 crit_enter();
  657 
  658                 if ( sc->qcount - l < 0 ) {
  659                         DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
  660                         sc->qcount = l;
  661                 }
  662                 sc->qcount -= l;        /* remove the bytes from the buffer */
  663                 sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
  664         }
  665         crit_exit();
  666 
  667         return 0;
  668 }
  669 
  670 static struct filterops ums_filtops =
  671         { FILTEROP_ISFD, NULL, ums_filt_detach, ums_filt };
  672 
  673 static int
  674 ums_kqfilter(struct dev_kqfilter_args *ap)
  675 {
  676         cdev_t dev = ap->a_head.a_dev;
  677         struct knote *kn = ap->a_kn;
  678         struct ums_softc *sc;
  679         struct klist *klist;
  680 
  681         ap->a_result = 0;
  682 
  683         switch (kn->kn_filter) {
  684         case EVFILT_READ:
  685                 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  686                 kn->kn_fop = &ums_filtops;
  687                 kn->kn_hook = (caddr_t)sc;
  688                 break;
  689         default:
  690                 ap->a_result = EOPNOTSUPP;
  691                 return (0);
  692         }
  693 
  694         klist = &sc->rkq.ki_note;
  695         knote_insert(klist, kn);
  696 
  697         return (0);
  698 }
  699 
  700 static void
  701 ums_filt_detach(struct knote *kn)
  702 {
  703         struct ums_softc *sc = (struct ums_softc *)kn->kn_hook;
  704         struct klist *klist;
  705 
  706         klist = &sc->rkq.ki_note;
  707         knote_remove(klist, kn);
  708 }
  709 
  710 static int
  711 ums_filt(struct knote *kn, long hint)
  712 {
  713         struct ums_softc *sc = (struct ums_softc *)kn->kn_hook;
  714         int ready = 0;
  715 
  716         crit_enter();
  717         if (sc->qcount)
  718                 ready = 1;
  719         crit_exit();
  720 
  721         return (ready);
  722 }
  723 
  724 int
  725 ums_ioctl(struct dev_ioctl_args *ap)
  726 {
  727         cdev_t dev = ap->a_head.a_dev;
  728         struct ums_softc *sc;
  729         int error = 0;
  730         mousemode_t mode;
  731 
  732         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
  733 
  734         if (!sc)
  735                 return EIO;
  736 
  737         switch(ap->a_cmd) {
  738         case MOUSE_GETHWINFO:
  739                 *(mousehw_t *)ap->a_data = sc->hw;
  740                 break;
  741         case MOUSE_GETMODE:
  742                 *(mousemode_t *)ap->a_data = sc->mode;
  743                 break;
  744         case MOUSE_SETMODE:
  745                 mode = *(mousemode_t *)ap->a_data;
  746 
  747                 if (mode.level == -1)
  748                         /* don't change the current setting */
  749                         ;
  750                 else if ((mode.level < 0) || (mode.level > 1))
  751                         return (EINVAL);
  752 
  753                 crit_enter();
  754                 sc->mode.level = mode.level;
  755 
  756                 if (sc->mode.level == 0) {
  757                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
  758                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
  759                         else
  760                                 sc->hw.buttons = sc->nbuttons;
  761                         sc->mode.protocol = MOUSE_PROTO_MSC;
  762                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
  763                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
  764                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
  765                 } else if (sc->mode.level == 1) {
  766                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
  767                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
  768                         else
  769                                 sc->hw.buttons = sc->nbuttons;
  770                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
  771                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
  772                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
  773                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
  774                 }
  775 
  776                 bzero(sc->qbuf, sizeof(sc->qbuf));
  777                 sc->qhead = sc->qtail = sc->qcount = 0;
  778                 crit_exit();
  779 
  780                 break;
  781         case MOUSE_GETLEVEL:
  782                 *(int *)ap->a_data = sc->mode.level;
  783                 break;
  784         case MOUSE_SETLEVEL:
  785                 if (*(int *)ap->a_data < 0 || *(int *)ap->a_data > 1)
  786                         return (EINVAL);
  787 
  788                 crit_enter();
  789                 sc->mode.level = *(int *)ap->a_data;
  790 
  791                 if (sc->mode.level == 0) {
  792                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
  793                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
  794                         else
  795                                 sc->hw.buttons = sc->nbuttons;
  796                         sc->mode.protocol = MOUSE_PROTO_MSC;
  797                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
  798                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
  799                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
  800                 } else if (sc->mode.level == 1) {
  801                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
  802                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
  803                         else
  804                                 sc->hw.buttons = sc->nbuttons;
  805                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
  806                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
  807                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
  808                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
  809                 }
  810 
  811                 bzero(sc->qbuf, sizeof(sc->qbuf));
  812                 sc->qhead = sc->qtail = sc->qcount = 0;
  813                 crit_exit();
  814 
  815                 break;
  816         case MOUSE_GETSTATUS: {
  817                 mousestatus_t *status = (mousestatus_t *) ap->a_data;
  818 
  819                 crit_enter();
  820                 *status = sc->status;
  821                 sc->status.obutton = sc->status.button;
  822                 sc->status.button = 0;
  823                 sc->status.dx = sc->status.dy = sc->status.dz = 0;
  824                 crit_exit();
  825 
  826                 if (status->dx || status->dy || status->dz)
  827                         status->flags |= MOUSE_POSCHANGED;
  828                 if (status->button != status->obutton)
  829                         status->flags |= MOUSE_BUTTONSCHANGED;
  830                 break;
  831                 }
  832         default:
  833                 error = ENOTTY;
  834         }
  835 
  836         return error;
  837 }
  838 
  839 DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, NULL);

Cache object: e36d801ae8d06e73b136727529c02e26


[ 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.