root/dev/usb/ums.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ums_match
  2. ums_attach
  3. ums_activate
  4. ums_detach
  5. ums_intr
  6. ums_enable
  7. ums_disable
  8. ums_ioctl

    1 /*      $OpenBSD: ums.c,v 1.25 2007/06/14 10:11:16 mbalmer Exp $ */
    2 /*      $NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $        */
    3 
    4 /*
    5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
    6  * All rights reserved.
    7  *
    8  * This code is derived from software contributed to The NetBSD Foundation
    9  * by Lennart Augustsson (lennart@augustsson.net) at
   10  * Carlstedt Research & Technology.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. All advertising materials mentioning features or use of this software
   21  *    must display the following acknowledgement:
   22  *        This product includes software developed by the NetBSD
   23  *        Foundation, Inc. and its contributors.
   24  * 4. Neither the name of The NetBSD Foundation nor the names of its
   25  *    contributors may be used to endorse or promote products derived
   26  *    from this software without specific prior written permission.
   27  *
   28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   38  * POSSIBILITY OF SUCH DAMAGE.
   39  */
   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/device.h>
   50 #include <sys/ioctl.h>
   51 #include <sys/tty.h>
   52 #include <sys/file.h>
   53 #include <sys/selinfo.h>
   54 #include <sys/proc.h>
   55 #include <sys/vnode.h>
   56 #include <sys/poll.h>
   57 
   58 #include <dev/usb/usb.h>
   59 #include <dev/usb/usbhid.h>
   60 
   61 #include <dev/usb/usbdi.h>
   62 #include <dev/usb/usbdi_util.h>
   63 #include <dev/usb/usbdevs.h>
   64 #include <dev/usb/usb_quirks.h>
   65 #include <dev/usb/uhidev.h>
   66 #include <dev/usb/hid.h>
   67 
   68 #include <dev/wscons/wsconsio.h>
   69 #include <dev/wscons/wsmousevar.h>
   70 
   71 #ifdef USB_DEBUG
   72 #define DPRINTF(x)      do { if (umsdebug) printf x; } while (0)
   73 #define DPRINTFN(n,x)   do { if (umsdebug>(n)) printf x; } while (0)
   74 int     umsdebug = 0;
   75 #else
   76 #define DPRINTF(x)
   77 #define DPRINTFN(n,x)
   78 #endif
   79 
   80 #define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i)
   81 
   82 #define UMSUNIT(s)      (minor(s))
   83 
   84 #define PS2LBUTMASK     x01
   85 #define PS2RBUTMASK     x02
   86 #define PS2MBUTMASK     x04
   87 #define PS2BUTMASK 0x0f
   88 
   89 #define MAX_BUTTONS     16      /* must not exceed size of sc_buttons */
   90 
   91 struct ums_softc {
   92         struct uhidev sc_hdev;
   93 
   94         struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_w;
   95         struct hid_location sc_loc_btn[MAX_BUTTONS];
   96 
   97         int sc_enabled;
   98 
   99         int flags;              /* device configuration */
  100 #define UMS_Z           0x01    /* z direction available */
  101 #define UMS_SPUR_BUT_UP 0x02    /* spurious button up events */
  102 #define UMS_REVZ        0x04    /* Z-axis is reversed */
  103 
  104         int nbuttons;
  105 
  106         u_int32_t sc_buttons;   /* mouse button status */
  107         struct device *sc_wsmousedev;
  108 
  109         char                    sc_dying;
  110 };
  111 
  112 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
  113 #define MOUSE_FLAGS (HIO_RELATIVE)
  114 
  115 void ums_intr(struct uhidev *addr, void *ibuf, u_int len);
  116 
  117 int     ums_enable(void *);
  118 void    ums_disable(void *);
  119 int     ums_ioctl(void *, u_long, caddr_t, int, struct proc *);
  120 
  121 const struct wsmouse_accessops ums_accessops = {
  122         ums_enable,
  123         ums_ioctl,
  124         ums_disable,
  125 };
  126 
  127 int ums_match(struct device *, void *, void *); 
  128 void ums_attach(struct device *, struct device *, void *); 
  129 int ums_detach(struct device *, int); 
  130 int ums_activate(struct device *, enum devact); 
  131 
  132 struct cfdriver ums_cd = { 
  133         NULL, "ums", DV_DULL 
  134 }; 
  135 
  136 const struct cfattach ums_ca = { 
  137         sizeof(struct ums_softc), 
  138         ums_match, 
  139         ums_attach, 
  140         ums_detach, 
  141         ums_activate, 
  142 };
  143 
  144 int
  145 ums_match(struct device *parent, void *match, void *aux)
  146 {
  147         struct usb_attach_arg *uaa = aux;
  148         struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
  149         int size;
  150         void *desc;
  151 
  152         uhidev_get_report_desc(uha->parent, &desc, &size);
  153         if (!hid_is_collection(desc, size, uha->reportid,
  154                                HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
  155                 return (UMATCH_NONE);
  156 
  157         return (UMATCH_IFACECLASS);
  158 }
  159 
  160 void
  161 ums_attach(struct device *parent, struct device *self, void *aux)
  162 {
  163         struct ums_softc *sc = (struct ums_softc *)self;
  164         struct usb_attach_arg *uaa = aux;
  165         struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
  166         struct wsmousedev_attach_args a;
  167         int size;
  168         void *desc;
  169         u_int32_t flags, quirks;
  170         int i;
  171         struct hid_location loc_btn;
  172 
  173         sc->sc_hdev.sc_intr = ums_intr;
  174         sc->sc_hdev.sc_parent = uha->parent;
  175         sc->sc_hdev.sc_report_id = uha->reportid;
  176 
  177         quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags;
  178         if (quirks & UQ_MS_REVZ)
  179                 sc->flags |= UMS_REVZ;
  180         if (quirks & UQ_SPUR_BUT_UP)
  181                 sc->flags |= UMS_SPUR_BUT_UP;
  182 
  183         uhidev_get_report_desc(uha->parent, &desc, &size);
  184 
  185         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
  186                uha->reportid, hid_input, &sc->sc_loc_x, &flags)) {
  187                 printf("\n%s: mouse has no X report\n",
  188                        sc->sc_hdev.sc_dev.dv_xname);
  189                 return;
  190         }
  191         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  192                 printf("\n%s: X report 0x%04x not supported\n",
  193                        sc->sc_hdev.sc_dev.dv_xname, flags);
  194                 return;
  195         }
  196 
  197         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
  198                uha->reportid, hid_input, &sc->sc_loc_y, &flags)) {
  199                 printf("\n%s: mouse has no Y report\n",
  200                        sc->sc_hdev.sc_dev.dv_xname);
  201                 return;
  202         }
  203         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  204                 printf("\n%s: Y report 0x%04x not supported\n",
  205                        sc->sc_hdev.sc_dev.dv_xname, flags);
  206                 return;
  207         }
  208 
  209         /* Try the wheel as Z activator first */
  210         if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
  211             uha->reportid, hid_input, &sc->sc_loc_z, &flags)) {
  212                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  213                         DPRINTF(("\n%s: Wheel report 0x%04x not supported\n",
  214                                 sc->sc_hdev.sc_dev.dv_xname, flags));
  215                         sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
  216                 } else {
  217                         sc->flags |= UMS_Z;
  218                         /* Wheels need the Z axis reversed. */
  219                         sc->flags ^= UMS_REVZ;
  220                 }
  221                 /*
  222                  * We might have both a wheel and Z direction; in this case,
  223                  * report the Z direction on the W axis.
  224                 */
  225                 if (hid_locate(desc, size,
  226                     HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
  227                     uha->reportid, hid_input, &sc->sc_loc_w, &flags)) {
  228                         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  229                                 DPRINTF(("\n%s: Z report 0x%04x not supported\n",
  230                                         sc->sc_hdev.sc_dev.dv_xname, flags));
  231                                 /* Bad Z coord, ignore it */
  232                                 sc->sc_loc_w.size = 0;
  233                         }
  234                 }
  235         } else if (hid_locate(desc, size,
  236             HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
  237             uha->reportid, hid_input, &sc->sc_loc_z, &flags)) {
  238                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
  239                         DPRINTF(("\n%s: Z report 0x%04x not supported\n",
  240                                 sc->sc_hdev.sc_dev.dv_xname, flags));
  241                         sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
  242                 } else {
  243                         sc->flags |= UMS_Z;
  244                 }
  245         }
  246 
  247         /* figure out the number of buttons */
  248         for (i = 1; i <= MAX_BUTTONS; i++)
  249                 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
  250                         uha->reportid, hid_input, &loc_btn, 0))
  251                         break;
  252         sc->nbuttons = i - 1;
  253 
  254         printf(": %d button%s%s\n",
  255             sc->nbuttons, sc->nbuttons == 1 ? "" : "s",
  256             sc->flags & UMS_Z ? " and Z dir." : "");
  257 
  258         for (i = 1; i <= sc->nbuttons; i++)
  259                 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
  260                            uha->reportid, hid_input,
  261                            &sc->sc_loc_btn[i-1], 0);
  262 
  263 #ifdef USB_DEBUG
  264         DPRINTF(("ums_attach: sc=%p\n", sc));
  265         DPRINTF(("ums_attach: X\t%d/%d\n",
  266                  sc->sc_loc_x.pos, sc->sc_loc_x.size));
  267         DPRINTF(("ums_attach: Y\t%d/%d\n",
  268                  sc->sc_loc_y.pos, sc->sc_loc_y.size));
  269         if (sc->flags & UMS_Z)
  270                 DPRINTF(("ums_attach: Z\t%d/%d\n",
  271                          sc->sc_loc_z.pos, sc->sc_loc_z.size));
  272         for (i = 1; i <= sc->nbuttons; i++) {
  273                 DPRINTF(("ums_attach: B%d\t%d/%d\n",
  274                          i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
  275         }
  276 #endif
  277 
  278         a.accessops = &ums_accessops;
  279         a.accesscookie = sc;
  280 
  281         sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
  282 }
  283 
  284 int
  285 ums_activate(struct device *self, enum devact act)
  286 {
  287         struct ums_softc *sc = (struct ums_softc *)self;
  288         int rv = 0;
  289 
  290         switch (act) {
  291         case DVACT_ACTIVATE:
  292                 break;
  293 
  294         case DVACT_DEACTIVATE:
  295                 if (sc->sc_wsmousedev != NULL)
  296                         rv = config_deactivate(sc->sc_wsmousedev);
  297                 sc->sc_dying = 1;
  298                 break;
  299         }
  300         return (rv);
  301 }
  302 
  303 int
  304 ums_detach(struct device *self, int flags)
  305 {
  306         struct ums_softc *sc = (struct ums_softc *)self;
  307         int rv = 0;
  308 
  309         DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags));
  310 
  311         /* No need to do reference counting of ums, wsmouse has all the goo. */
  312         if (sc->sc_wsmousedev != NULL)
  313                 rv = config_detach(sc->sc_wsmousedev, flags);
  314 
  315         return (rv);
  316 }
  317 
  318 void
  319 ums_intr(struct uhidev *addr, void *ibuf, u_int len)
  320 {
  321         struct ums_softc *sc = (struct ums_softc *)addr;
  322         int dx, dy, dz, dw;
  323         u_int32_t buttons = 0;
  324         int i;
  325         int s;
  326 
  327         DPRINTFN(5,("ums_intr: len=%d\n", len));
  328 
  329         dx =  hid_get_data(ibuf, &sc->sc_loc_x);
  330         dy = -hid_get_data(ibuf, &sc->sc_loc_y);
  331         dz =  hid_get_data(ibuf, &sc->sc_loc_z);
  332         dw =  hid_get_data(ibuf, &sc->sc_loc_w);
  333         if (sc->flags & UMS_REVZ)
  334                 dz = -dz;
  335         for (i = 0; i < sc->nbuttons; i++)
  336                 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
  337                         buttons |= (1 << UMS_BUT(i));
  338 
  339         if (dx != 0 || dy != 0 || dz != 0 || dw != 0 ||
  340             buttons != sc->sc_buttons) {
  341                 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d w:%d buttons:0x%x\n",
  342                         dx, dy, dz, dw, buttons));
  343                 sc->sc_buttons = buttons;
  344                 if (sc->sc_wsmousedev != NULL) {
  345                         s = spltty();
  346                         wsmouse_input(sc->sc_wsmousedev, buttons,
  347                             dx, dy, dz, dw, WSMOUSE_INPUT_DELTA);
  348                         splx(s);
  349                 }
  350         }
  351 }
  352 
  353 int
  354 ums_enable(void *v)
  355 {
  356         struct ums_softc *sc = v;
  357 
  358         DPRINTFN(1,("ums_enable: sc=%p\n", sc));
  359 
  360         if (sc->sc_dying)
  361                 return (EIO);
  362 
  363         if (sc->sc_enabled)
  364                 return (EBUSY);
  365 
  366         sc->sc_enabled = 1;
  367         sc->sc_buttons = 0;
  368 
  369         return (uhidev_open(&sc->sc_hdev));
  370 }
  371 
  372 void
  373 ums_disable(void *v)
  374 {
  375         struct ums_softc *sc = v;
  376 
  377         DPRINTFN(1,("ums_disable: sc=%p\n", sc));
  378 #ifdef DIAGNOSTIC
  379         if (!sc->sc_enabled) {
  380                 printf("ums_disable: not enabled\n");
  381                 return;
  382         }
  383 #endif
  384 
  385         sc->sc_enabled = 0;
  386         uhidev_close(&sc->sc_hdev);
  387 }
  388 
  389 int
  390 ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
  391 
  392 {
  393         switch (cmd) {
  394         case WSMOUSEIO_GTYPE:
  395                 *(u_int *)data = WSMOUSE_TYPE_USB;
  396                 return (0);
  397         }
  398 
  399         return (-1);
  400 }

/* [<][>][^][v][top][bottom][index][help] */