File:  [DragonFly] / src / sys / dev / usbmisc / ums / ums.c
Revision 1.12: download - view: text, annotated - select for diffs
Wed May 19 22:52:51 2004 UTC (9 years, 11 months ago) by dillon
Branches: MAIN
CVS tags: HEAD, DragonFly_Snap13Sep2004, DragonFly_1_0_REL, DragonFly_1_0_RC1, DragonFly_1_0A_REL
Device layer rollup commit.

* cdevsw_add() is now required.  cdevsw_add() and cdevsw_remove() may specify
  a mask/match indicating the range of supported minor numbers.  Multiple
  cdevsw_add()'s using the same major number, but distinctly different
  ranges, may be issued.  All devices that failed to call cdevsw_add() before
  now do.

* cdevsw_remove() now automatically marks all devices within its supported
  range as being destroyed.

* vnode->v_rdev is no longer resolved when the vnode is created.  Instead,
  only v_udev (a newly added field) is resolved.  v_rdev is resolved when
  the vnode is opened and cleared on the last close.

* A great deal of code was making rather dubious assumptions with regards
  to the validity of devices associated with vnodes, primarily due to
  the persistence of a device structure due to being indexed by (major, minor)
  instead of by (cdevsw, major, minor).  In particular, if you run a program
  which connects to a USB device and then you pull the USB device and plug
  it back in, the vnode subsystem will continue to believe that the device
  is open when, in fact, it isn't (because it was destroyed and recreated).

  In particular, note that all the VFS mount procedures now check devices
  via v_udev instead of v_rdev prior to calling VOP_OPEN(), since v_rdev
  is NULL prior to the first open.

* The disk layer's device interaction has been rewritten.  The disk layer
  (i.e. the slice and disklabel management layer) no longer overloads
  its data onto the device structure representing the underlying physical
  disk.  Instead, the disk layer uses the new cdevsw_add() functionality
  to register its own cdevsw using the underlying device's major number,
  and simply does NOT register the underlying device's cdevsw.  No
  confusion is created because the device hash is now based on
  (cdevsw,major,minor) rather then (major,minor).

  NOTE: This also means that underlying raw disk devices may use the entire
  device minor number instead of having to reserve the bits used by the disk
  layer, and also means that can we (theoretically) stack a fully
  disklabel-supported 'disk' on top of any block device.

* The new reference counting scheme prevents this by associating a device
  with a cdevsw and disconnecting the device from its cdevsw when the cdevsw
  is removed.  Additionally, all udev2dev() lookups run through the cdevsw
  mask/match and only successfully find devices still associated with an
  active cdevsw.

* Major work on MFS:  MFS no longer shortcuts vnode and device creation.  It
  now creates a real vnode and a real device and implements real open and
  close VOPs.  Additionally, due to the disk layer changes, MFS is no longer
  limited to 255 mounts.  The new limit is 16 million.  Since MFS creates a
  real device node, mount_mfs will now create a real /dev/mfs<PID> device
  that can be read from userland (e.g. so you can dump an MFS filesystem).

* BUF AND DEVICE STRATEGY changes.  The struct buf contains a b_dev field.
  In order to properly handle stacked devices we now require that the b_dev
  field be initialized before the device strategy routine is called.  This
  required some additional work in various VFS implementations.  To enforce
  this requirement, biodone() now sets b_dev to NODEV.  The new disk layer
  will adjust b_dev before forwarding a request to the actual physical
  device.

* A bug in the ISO CD boot sequence which resulted in a panic has been fixed.

Testing by: lots of people, but David Rhodus found the most aggregious bugs.

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