PERFORCE change 98544 for review

Hans Petter Selasky hselasky at FreeBSD.org
Mon Jun 5 12:50:07 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=98544

Change 98544 by hselasky at hselasky_mini_itx on 2006/06/05 12:47:48

	Commit reworked UHID driver.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/uhid.c#4 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_rdesc.h#1 add
.. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#3 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/uhid.c#4 (text+ko) ====

@@ -1,0 +1,846 @@
+/*	$NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $	*/
+
+/* Also already merged from NetBSD:
+ *	$NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $
+ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart at augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb_port.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_subr.h>
+#include <dev/usb/usb_hid.h>
+#include <dev/usb/usb_rdesc.h>
+#include <dev/usb/usb_quirks.h>
+
+#include "usbdevs.h"
+
+__FBSDID("$FreeBSD: src/sys/dev/usb/uhid.c $");
+
+#ifdef USB_DEBUG
+#define DPRINTF(n,fmt,...)						\
+  do { if (uhid_debug > (n)) {						\
+      printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0)
+
+static int uhid_debug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid");
+SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RW,
+	   &uhid_debug, 0, "uhid debug level");
+#else
+#define DPRINTF(...)
+#endif
+
+/* temporary compile hacks for old USB systems: */
+
+#ifndef UQ_HID_IGNORE
+#define UQ_HID_IGNORE 0
+#endif
+
+#ifndef USB_PRODUCT_WACOM_GRAPHIRE3_4X5
+#define USB_PRODUCT_WACOM_GRAPHIRE3_4X5 0
+#endif
+
+#define UHID_N_TRANSFER    5 /* units */
+#define	UHID_BSIZE	1024 /* bytes, buffer size */
+#define	UHID_FRAME_NUM 	  50 /* bytes, frame number */
+
+struct uhid_softc {
+	struct usb_cdev   sc_cdev;
+	struct mtx        sc_mtx;
+
+	struct usbd_xfer *      sc_xfer[UHID_N_TRANSFER];
+	void *                  sc_repdesc_ptr;
+
+	u_int32_t sc_isize;
+	u_int32_t sc_osize;
+	u_int32_t sc_fsize;
+	u_int32_t sc_repdesc_size;
+	u_int32_t sc_transfer_len;
+
+	u_int8_t sc_transfer_buf[sizeof(usb_device_request_t) + UHID_BSIZE];
+	u_int8_t sc_iface_no;
+	u_int8_t sc_iid;
+	u_int8_t sc_oid;
+	u_int8_t sc_fid;
+	u_int8_t sc_wakeup_detach;
+	u_int8_t sc_flags;
+#define UHID_FLAG_IMMED        0x01 /* set if read should be immediate */
+#define UHID_FLAG_INTR_STALLED 0x02 /* set if interrup transfer stalled */
+#define UHID_FLAG_STATIC_DESC  0x04 /* set if report descriptors are static */
+#define UHID_FLAG_COMMAND_ERR  0x08 /* set if control transfer had an error */
+#define UHID_FLAG_WAIT_USB     0x10 /* set if should wait for USB */
+};
+
+static u_int8_t uhid_xb360gp_report_descr[] = { UHID_XB360GP_REPORT_DESCR() };
+static u_int8_t uhid_graphire_report_descr[] = { UHID_GRAPHIRE_REPORT_DESCR() };
+static u_int8_t uhid_graphire3_4x5_report_descr[] = { UHID_GRAPHIRE3_4X5_REPORT_DESCR() };
+
+static void
+uhid_intr_callback(struct usbd_xfer *xfer)
+{
+	struct uhid_softc *sc = xfer->priv_sc;
+	struct usbd_mbuf *m;
+
+	USBD_CHECK_STATUS(xfer);
+
+ tr_transferred:
+	DPRINTF(0, "transferred!\n");
+
+	if (xfer->actlen == sc->sc_isize) {
+	    usb_cdev_put_data(&(sc->sc_cdev), 
+			      xfer->buffer, xfer->actlen, 1);
+	} else {
+	    /* ignore it */
+	    DPRINTF(0, "ignored short transfer, %d bytes\n",
+		    xfer->actlen);
+	}
+
+ tr_setup:
+	USBD_IF_POLL(&(sc->sc_cdev.sc_rdq_free), m);
+
+	if ((!(sc->sc_flags & (UHID_FLAG_INTR_STALLED))) && m) {
+	    xfer->length = sc->sc_isize;
+	    usbd_start_hardware(xfer);
+	}
+	return;
+
+ tr_error:
+	if (xfer->error != USBD_CANCELLED) {
+	    /* try to clear stall first */
+	    usbd_transfer_start(sc->sc_xfer[1]);
+	}
+	return;
+}
+
+static void
+uhid_intr_clear_stall_callback(struct usbd_xfer *xfer)
+{
+	struct uhid_softc *sc = xfer->priv_sc;
+	USBD_CHECK_STATUS(xfer);
+
+ tr_setup:
+	/* start clear stall */
+	sc->sc_flags |= UHID_FLAG_INTR_STALLED;
+	usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]);
+	return;
+
+ tr_transferred:
+	usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]);
+
+	sc->sc_flags &= ~UHID_FLAG_INTR_STALLED;
+	usbd_transfer_start(sc->sc_xfer[0]);
+	return;
+
+ tr_error:
+	/* bomb out */
+	sc->sc_flags &= ~UHID_FLAG_INTR_STALLED;
+	usb_cdev_put_data_error(&(sc->sc_cdev));
+	return;
+}
+
+static void
+uhid_write_callback(struct usbd_xfer *xfer)
+{
+	struct uhid_softc *sc = xfer->priv_sc;
+	usb_device_request_t *req = xfer->buffer;
+	u_int32_t size = sc->sc_osize;
+	u_int32_t actlen;
+	u_int8_t id;
+
+	USBD_CHECK_STATUS(xfer);
+
+ tr_transferred:
+ tr_setup:
+	/* try to extract the ID byte */
+	if (sc->sc_oid) {
+
+	    if (usb_cdev_get_data(&(sc->sc_cdev), &id, 1, &actlen, 0)) {
+	        if (actlen != 1) {
+		    goto tr_error;
+		}
+	    } else {
+	      return;
+	    }
+	    if (size) {
+	        size--;
+	    }
+	} else {
+	  id = 0;
+	}
+
+	if (usb_cdev_get_data(&(sc->sc_cdev), req->bData, 
+			      UHID_BSIZE, &actlen, 1)) {
+	    if (actlen != size) {
+	        goto tr_error;
+	    }
+	    usbd_fill_set_report
+	      (req, sc->sc_iface_no,
+	       UHID_OUTPUT_REPORT, id, size);
+
+	    xfer->length = sizeof(*req) + size;
+
+	    usbd_start_hardware(xfer);
+	}
+	return;
+
+ tr_error:
+	/* bomb out */
+	usb_cdev_get_data_error(&(sc->sc_cdev));
+	return;
+}
+
+static void
+uhid_read_callback(struct usbd_xfer *xfer)
+{
+	struct uhid_softc *sc = xfer->priv_sc;
+	usb_device_request_t *req = xfer->buffer;
+	struct usbd_mbuf *m;
+
+	USBD_CHECK_STATUS(xfer);
+
+ tr_transferred:
+	usb_cdev_put_data(&(sc->sc_cdev), req->bData, sc->sc_isize, 1);
+	return;
+
+ tr_setup:
+	USBD_IF_POLL(&(sc->sc_cdev.sc_rdq_free), m);
+
+	if (m) {
+	  usbd_fill_get_report
+	    (req, sc->sc_iface_no, UHID_INPUT_REPORT, 
+	     sc->sc_iid, sc->sc_isize);
+
+	  xfer->length = sizeof(*req) + sc->sc_isize;
+
+	  usbd_start_hardware(xfer);
+	}
+	return;
+
+ tr_error:
+	/* bomb out */
+	usb_cdev_put_data_error(&(sc->sc_cdev));
+	return;
+}
+
+static void
+uhid_ioctl_callback(struct usbd_xfer *xfer)
+{
+	struct uhid_softc *sc = xfer->priv_sc;
+
+	USBD_CHECK_STATUS(xfer);
+
+ tr_transferred:
+	bcopy(xfer->buffer, sc->sc_transfer_buf, sc->sc_transfer_len);
+	sc->sc_flags &= ~UHID_FLAG_COMMAND_ERR;
+	usb_cdev_wakeup(&(sc->sc_cdev));
+	return;
+
+ tr_error:
+	DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error));
+	sc->sc_flags |= UHID_FLAG_COMMAND_ERR;
+	usb_cdev_wakeup(&(sc->sc_cdev));
+	return;
+
+ tr_setup:
+	bcopy(sc->sc_transfer_buf, xfer->buffer, sc->sc_transfer_len);
+	xfer->length = sc->sc_transfer_len;
+	usbd_start_hardware(xfer);
+	return;
+}
+
+static const struct usbd_config uhid_config[UHID_N_TRANSFER] = {
+
+    [0] = {
+      .type      = UE_INTERRUPT,
+      .endpoint  = -1, /* any */
+      .direction = UE_DIR_IN,
+      .flags     = USBD_SHORT_XFER_OK,
+      .bufsize   = UHID_BSIZE, /* bytes */
+      .callback  = &uhid_intr_callback,
+    },
+
+    [1] = {
+      .type      = UE_CONTROL,
+      .endpoint  = 0x00, /* Control pipe */
+      .direction = -1,
+      .bufsize   = sizeof(usb_device_request_t),
+      .callback  = &uhid_intr_clear_stall_callback,
+      .timeout   = 1000, /* 1 second */
+    },
+
+    [2] = {
+      .type      = UE_CONTROL,
+      .endpoint  = 0x00, /* Control pipe */
+      .direction = -1,
+      .bufsize   = sizeof(usb_device_request_t) + UHID_BSIZE,
+      .callback  = &uhid_write_callback,
+      .timeout   = 1000, /* 1 second */
+    },
+
+    [3] = {
+      .type      = UE_CONTROL,
+      .endpoint  = 0x00, /* Control pipe */
+      .direction = -1,
+      .bufsize   = sizeof(usb_device_request_t) + UHID_BSIZE,
+      .callback  = &uhid_read_callback,
+      .timeout   = 1000, /* 1 second */
+    },
+
+    [4] = {
+      .type      = UE_CONTROL,
+      .endpoint  = 0x00, /* Control pipe */
+      .direction = -1,
+      .bufsize   = sizeof(usb_device_request_t) + UHID_BSIZE,
+      .callback  = &uhid_ioctl_callback,
+      .timeout   = 1000, /* 1 second */
+    },
+};
+
+static void
+uhid_start_read(struct usb_cdev *cdev)
+{
+	struct uhid_softc *sc = cdev->sc_priv_ptr;
+
+	if (sc->sc_flags & UHID_FLAG_IMMED) {
+	    usbd_transfer_start(sc->sc_xfer[3]);
+	} else {
+	    usbd_transfer_start(sc->sc_xfer[0]);
+	}
+	return;
+}
+
+static void
+uhid_stop_read(struct usb_cdev *cdev)
+{
+	struct uhid_softc *sc = cdev->sc_priv_ptr;
+
+	usbd_transfer_stop(sc->sc_xfer[3]);
+	usbd_transfer_stop(sc->sc_xfer[0]);
+	return;
+}
+
+static void
+uhid_start_write(struct usb_cdev *cdev)
+{
+	struct uhid_softc *sc = cdev->sc_priv_ptr;
+
+	usbd_transfer_start(sc->sc_xfer[2]);
+	return;
+}
+
+static void
+uhid_stop_write(struct usb_cdev *cdev)
+{
+	struct uhid_softc *sc = cdev->sc_priv_ptr;
+
+	usbd_transfer_stop(sc->sc_xfer[2]);
+	return;
+}
+
+static int32_t
+uhid_do_control_transfer(struct uhid_softc *sc, int32_t fflags)
+{
+	int32_t error;
+
+	sc->sc_flags |=  UHID_FLAG_COMMAND_ERR;
+
+	usbd_transfer_start(sc->sc_xfer[4]);
+
+	error = usb_cdev_sleep(&(sc->sc_cdev), fflags);
+
+	usbd_transfer_stop(sc->sc_xfer[4]);
+
+	if (error) {
+	    return error;
+	}
+
+	if (sc->sc_flags & UHID_FLAG_COMMAND_ERR) {
+	    return ENXIO;
+	}
+	return 0;
+}
+
+static int32_t
+uhid_get_report(struct uhid_softc *sc, int32_t fflags, 
+		u_int8_t type, u_int8_t id, void *data, u_int16_t len)
+{
+	usb_device_request_t *req = (void *)(sc->sc_transfer_buf);
+	int error;
+
+	if (len > UHID_BSIZE) {
+	    len = UHID_BSIZE;
+	}
+
+	usbd_fill_get_report
+	  (req, sc->sc_iface_no, type, id, len);
+
+	sc->sc_transfer_len = sizeof(*req) + len;
+
+	error = uhid_do_control_transfer(sc, fflags);
+
+	if (data) {
+	    bcopy(req->bData, data, len);
+	}
+	return error;
+}
+
+static int32_t
+uhid_set_report(struct uhid_softc *sc, int32_t fflags, 
+		u_int8_t type, u_int8_t id, void *data, u_int16_t len)
+{
+	usb_device_request_t *req = (void *)(sc->sc_transfer_buf);
+
+	if (len > UHID_BSIZE) {
+	    len = UHID_BSIZE;
+	}
+
+	usbd_fill_set_report
+	  (req, sc->sc_iface_no, type, id, len);
+
+	bcopy(data, req->bData, len);
+
+	sc->sc_transfer_len = sizeof(*req) + len;
+
+	return uhid_do_control_transfer(sc, fflags);
+}
+
+static int32_t
+uhid_open(struct usb_cdev *cdev, int32_t fflags, 
+	  int32_t devtype, struct thread *td)
+{
+	struct uhid_softc *sc = cdev->sc_priv_ptr;
+
+	if (fflags & FREAD) {
+	    /* reset flags */
+	    sc->sc_flags &= ~UHID_FLAG_IMMED;
+	}
+	return 0;
+}
+
+static int32_t
+uhid_ioctl(struct usb_cdev *cdev, u_long cmd, caddr_t addr, 
+	   int32_t fflags, struct thread *td)
+{
+	struct uhid_softc *sc = cdev->sc_priv_ptr;
+	struct usb_ctl_report_desc *rd;
+	struct usb_ctl_report *re;
+	u_int32_t size;
+	int32_t error = 0;
+	u_int8_t id;
+
+	switch (cmd) {
+	case USB_GET_REPORT_DESC:
+		rd = (void *)addr;
+		size = min(sc->sc_repdesc_size, sizeof(rd->ucrd_data));
+		rd->ucrd_size = size;
+		bcopy(sc->sc_repdesc_ptr, rd->ucrd_data, size);
+		break;
+
+	case USB_SET_IMMED:
+
+		if (!(fflags & FREAD)) {
+		    error = EPERM;
+		    goto done;
+		}
+
+		if (*(int *)addr) {
+
+		    /* do a test read */
+
+		    error = uhid_get_report(sc, fflags, UHID_INPUT_REPORT,
+					    sc->sc_iid, NULL, sc->sc_isize);
+		    if (error) {
+		        goto done;
+		    }
+		    sc->sc_flags |=  UHID_FLAG_IMMED;
+		} else {
+		    sc->sc_flags &= ~UHID_FLAG_IMMED;
+		}
+		break;
+
+	case USB_GET_REPORT:
+
+		if (!(fflags & FREAD)) {
+		    error = EPERM;
+		    goto done;
+		}
+
+		re = (void *)addr;
+		switch (re->ucr_report) {
+		case UHID_INPUT_REPORT:
+			size = sc->sc_isize;
+			id = sc->sc_iid;
+			break;
+		case UHID_OUTPUT_REPORT:
+			size = sc->sc_osize;
+			id = sc->sc_oid;
+			break;
+		case UHID_FEATURE_REPORT:
+			size = sc->sc_fsize;
+			id = sc->sc_fid;
+			break;
+		default:
+			error = EINVAL;
+			goto done;
+		}
+		error = uhid_get_report(sc, fflags, re->ucr_report, id, 
+					re->ucr_data, size);
+		if (error) {
+		    goto done;
+		}
+		break;
+
+	case USB_SET_REPORT:
+
+		if (!(fflags & FWRITE)) {
+		    error = EPERM;
+		    goto done;
+		}
+
+		re = (void *)addr;
+		switch (re->ucr_report) {
+		case UHID_INPUT_REPORT:
+			size = sc->sc_isize;
+			id = sc->sc_iid;
+			break;
+		case UHID_OUTPUT_REPORT:
+			size = sc->sc_osize;
+			id = sc->sc_oid;
+			break;
+		case UHID_FEATURE_REPORT:
+			size = sc->sc_fsize;
+			id = sc->sc_fid;
+			break;
+		default:
+			return (EINVAL);
+		}
+		error = uhid_set_report(sc, fflags, re->ucr_report, id, 
+					re->ucr_data, size);
+		if (error) {
+		  goto done;
+		}
+		break;
+
+	case USB_GET_REPORT_ID:
+		*(int *)addr = 0; /* XXX: we only support reportid 0? */
+		break;
+
+	default:
+		error = EINVAL;
+		break;
+	}
+
+ done:
+	return error;
+}
+
+static device_probe_t uhid_probe;
+static device_attach_t uhid_attach;
+static device_detach_t uhid_detach;
+
+static int
+uhid_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	usb_interface_descriptor_t *id;
+
+	DPRINTF(10, "\n");
+
+	if (uaa->iface == NULL) {
+	    return UMATCH_NONE;
+	}
+
+	id = usbd_get_interface_descriptor(uaa->iface);
+	if (id == NULL) {
+	    return UMATCH_NONE;
+	}
+
+	if  (id->bInterfaceClass != UICLASS_HID) {
+
+	    /* the Xbox 360 gamepad doesn't use the HID class */
+
+	    if ((id->bInterfaceClass != UICLASS_VENDOR) ||
+		(id->bInterfaceSubClass != UISUBCLASS_XBOX360_CONTROLLER) ||
+		(id->bInterfaceProtocol != UIPROTO_XBOX360_GAMEPAD)) {
+	        return UMATCH_NONE;
+	    }
+	}
+
+	if (usbd_get_quirks(uaa->device)->uq_flags & UQ_HID_IGNORE) {
+	    return UMATCH_NONE;
+	}
+
+	return UMATCH_IFACECLASS_GENERIC;
+}
+
+static void
+uhid_detach_complete(struct usbd_memory_info *info)
+{
+	struct uhid_softc *sc = info->priv_sc;
+
+	mtx_lock(&(sc->sc_mtx));
+
+	if (sc->sc_flags &   UHID_FLAG_WAIT_USB) {
+	    sc->sc_flags &= ~UHID_FLAG_WAIT_USB;
+	    wakeup(&(sc->sc_wakeup_detach));
+	}
+
+	mtx_unlock(&(sc->sc_mtx));
+
+	return;
+}
+
+static int
+uhid_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uhid_softc *sc = device_get_softc(dev);
+	usb_interface_descriptor_t *id = 
+	  usbd_get_interface_descriptor(uaa->iface);
+	const char * p_buf[2];
+	int32_t unit = device_get_unit(dev);
+	int32_t error = 0;
+	char buf[16];
+
+	DPRINTF(10, "sc=%p\n", sc);
+
+	if (sc == NULL) {
+	    return ENOMEM;
+	}
+
+	usbd_set_desc(dev, uaa->device);
+
+	mtx_init(&(sc->sc_mtx), "uhid lock", NULL, MTX_DEF|MTX_RECURSE);
+
+	sc->sc_iface_no = uaa->iface->idesc->bInterfaceNumber;
+
+	error = usbd_transfer_setup(uaa->device, uaa->iface_index, 
+				    sc->sc_xfer, uhid_config, UHID_N_TRANSFER,
+				    sc, &(sc->sc_mtx), &(uhid_detach_complete));
+	if (error) {
+	    DPRINTF(0, "error=%s\n", usbd_errstr(error)) ;
+	    goto detach;
+	}
+
+	sc->sc_flags |= UHID_FLAG_WAIT_USB;
+
+	if (uaa->vendor == USB_VENDOR_WACOM) {
+
+	    /* the report descriptor for the Wacom Graphire is broken */
+
+	    if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE) {
+
+	        sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr);
+		sc->sc_repdesc_ptr = uhid_graphire_report_descr;
+		sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+
+	    } else if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) {
+
+	        static u_int8_t reportbuf[] = { 2, 2, 2 };
+
+		/*
+		 * The Graphire3 needs 0x0202 to be written to
+		 * feature report ID 2 before it'll start
+		 * returning digitizer data.
+		 */
+		error = usbreq_set_report
+		  (uaa->device, uaa->iface_index, 
+		   UHID_FEATURE_REPORT, 2,
+		   reportbuf, sizeof(reportbuf));
+
+		if (error) {
+		    DPRINTF(0, "set report failed, error=%s (ignored)\n", 
+			    usbd_errstr(error));
+		}
+
+		sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr);
+		sc->sc_repdesc_ptr = uhid_graphire3_4x5_report_descr;
+		sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+	    }
+	} else if ((id->bInterfaceClass == UICLASS_VENDOR) &&
+		   (id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) &&
+		   (id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) {
+
+	    /* the Xbox 360 gamepad has no report descriptor */
+	    sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr);
+	    sc->sc_repdesc_ptr = uhid_xb360gp_report_descr;
+	    sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+	}
+
+	if (sc->sc_repdesc_ptr == NULL) {
+
+	    error = usbreq_read_report_desc
+	      (uaa->device, uaa->iface_index, 
+	       &(sc->sc_repdesc_ptr), &(sc->sc_repdesc_size), M_USBDEV);
+
+	    if (error) {
+	        device_printf(dev, "no report descriptor\n");
+		goto detach;
+	    }
+	}
+
+	error = usbreq_set_idle(uaa->device, uaa->iface_index, 0, 0);
+
+	if (error) {
+	    DPRINTF(0, "set idle failed, error=%s (ignored)\n", 
+		    usbd_errstr(error));
+	}
+
+	sc->sc_isize = hid_report_size
+	  (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input,   &sc->sc_iid);
+
+	sc->sc_osize = hid_report_size
+	  (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output,  &sc->sc_oid);
+
+	sc->sc_fsize = hid_report_size
+	  (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid);
+
+	if (sc->sc_isize > UHID_BSIZE) {
+	    DPRINTF(0, "input size is too large, "
+		    "%d bytes (truncating)\n", 
+		    sc->sc_isize);
+	    sc->sc_isize = UHID_BSIZE;
+	}
+	if (sc->sc_osize > UHID_BSIZE) {
+	    DPRINTF(0, "output size is too large, "
+		    "%d bytes (truncating)\n", 
+		    sc->sc_osize);
+	    sc->sc_osize = UHID_BSIZE;
+	}
+	if (sc->sc_fsize > UHID_BSIZE) {
+	    DPRINTF(0, "feature size is too large, "
+		    "%d bytes (truncating)\n", 
+		    sc->sc_fsize);
+	    sc->sc_fsize = UHID_BSIZE;
+	}
+
+	snprintf(buf, sizeof(buf), "uhid%d", unit);
+
+	p_buf[0] = buf;
+	p_buf[1] = NULL;
+
+	sc->sc_cdev.sc_start_read = &uhid_start_read;
+	sc->sc_cdev.sc_start_write = &uhid_start_write;
+	sc->sc_cdev.sc_stop_read = &uhid_stop_read;
+	sc->sc_cdev.sc_stop_write = &uhid_stop_write;
+	sc->sc_cdev.sc_open = &uhid_open;
+	sc->sc_cdev.sc_ioctl = &uhid_ioctl;
+	sc->sc_cdev.sc_flags |= (USB_CDEV_FLAG_FWD_SHORT|
+				 USB_CDEV_FLAG_WAKEUP_RD_IMMED|
+				 USB_CDEV_FLAG_WAKEUP_WR_IMMED);
+
+	/* make the buffers one byte larger than maximum so
+	 * that one can detect too large read/writes and 
+	 * short transfers:
+	 */
+	error = usb_cdev_attach(&(sc->sc_cdev), sc, &(sc->sc_mtx), p_buf,
+				UID_ROOT, GID_OPERATOR, 0644, 
+				sc->sc_isize+1, UHID_FRAME_NUM,
+				sc->sc_osize+1, UHID_FRAME_NUM);
+	if (error) {
+	    goto detach;
+	}
+	return 0; /* success */
+
+ detach:
+	uhid_detach(dev);
+	return ENOMEM;
+}
+
+static int
+uhid_detach(device_t dev)
+{
+	struct uhid_softc *sc = device_get_softc(dev);
+	int32_t error;
+
+	usb_cdev_detach(&(sc->sc_cdev));
+
+	usbd_transfer_unsetup(sc->sc_xfer, UHID_N_TRANSFER);
+
+	if (sc->sc_repdesc_ptr) {
+	    if (!(sc->sc_flags & UHID_FLAG_STATIC_DESC)) {
+	        free(sc->sc_repdesc_ptr, M_USBDEV);
+	    }
+	}
+
+	mtx_lock(&(sc->sc_mtx));
+	while (sc->sc_flags & UHID_FLAG_WAIT_USB) {
+
+	    error = msleep(&(sc->sc_wakeup_detach), &(sc->sc_mtx), 
+			   PRIBIO, "uhid_sync", 0);
+	}
+	mtx_unlock(&(sc->sc_mtx));
+
+	mtx_destroy(&(sc->sc_mtx));
+
+	return 0;
+}
+
+static devclass_t uhid_devclass;
+
+static device_method_t uhid_methods[] = {
+    DEVMETHOD(device_probe, uhid_probe),
+    DEVMETHOD(device_attach, uhid_attach),
+    DEVMETHOD(device_detach, uhid_detach),
+    { 0, 0 }
+};
+
+static driver_t uhid_driver = {
+  .name    = "uhid",
+  .methods = uhid_methods,
+  .size    = sizeof(struct uhid_softc),
+};
+
+DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uhid, usb, 1, 1, 1);

==== //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#3 (text+ko) ====

@@ -45,6 +45,7 @@
 #include <dev/usb/usb_port.h>
 #include <dev/usb/usb.h>
 #include <dev/usb/usb_subr.h>
+#include <dev/usb/usb_hid.h>
 
 __FBSDID("$FreeBSD: src/sys/dev/usb2/usb_transfer.c $");
 
@@ -1056,42 +1057,93 @@
 }
 
 void
-usbd_clearstall_callback(struct usbd_xfer *xfer)
+usbd_fill_get_report(usb_device_request_t *req, u_int8_t iface_no, 
+		     u_int8_t type, u_int8_t id, u_int16_t size)
+{
+        req->bmRequestType = UT_READ_CLASS_INTERFACE;
+        req->bRequest = UR_GET_REPORT;
+        USETW2(req->wValue, type, id);
+        USETW(req->wIndex, iface_no);
+        USETW(req->wLength, size);
+	return;
+}
+
+void
+usbd_fill_set_report(usb_device_request_t *req, u_int8_t iface_no,
+		     u_int8_t type, u_int8_t id, u_int16_t size)
+{
+        req->bmRequestType = UT_WRITE_CLASS_INTERFACE;
+        req->bRequest = UR_SET_REPORT;
+        USETW2(req->wValue, type, id);
+        USETW(req->wIndex, iface_no);
+        USETW(req->wLength, size);
+	return;
+}
+
+void
+usbd_clear_stall_tr_setup(struct usbd_xfer *xfer1, 
+			  struct usbd_xfer *xfer2)
 {
-	usb_device_request_t *req;
-	USBD_CHECK_STATUS(xfer);
+	usb_device_request_t *req = xfer1->buffer;
+
+	mtx_assert(xfer1->priv_mtx, MA_OWNED);
+	mtx_assert(xfer2->priv_mtx, MA_OWNED);
 
- tr_setup:
-	req = xfer->buffer;
+	/* setup a clear-stall packet */
 
-	/* setup a CLEAR STALL packet */
 	req->bmRequestType = UT_WRITE_ENDPOINT;
 	req->bRequest = UR_CLEAR_FEATURE;
 	USETW(req->wValue, UF_ENDPOINT_HALT);
-	req->wIndex[0] = ((struct usbd_xfer *)(xfer->priv_sc))->pipe
-	  ->edesc->bEndpointAddress;
+	req->wIndex[0] = xfer2->pipe->edesc->bEndpointAddress;
 	req->wIndex[1] = 0;
 	USETW(req->wLength, 0);
 
-	usbd_start_hardware(xfer);
+	usbd_start_hardware(xfer1);
+	return;
+}
+
+void
+usbd_clear_stall_tr_transferred(struct usbd_xfer *xfer1, 
+				struct usbd_xfer *xfer2)
+{
+	mtx_assert(xfer1->priv_mtx, MA_OWNED);
+	mtx_assert(xfer2->priv_mtx, MA_OWNED);
+
+	mtx_lock(xfer2->usb_mtx);
+
+	/* 
+	 * clear any stall and make sure 
+	 * that DATA0 toggle will be 
+	 * used next:
+	 */
+
+	xfer2->pipe->clearstall = 0;
+	xfer2->pipe->toggle_next = 0;
+
+	mtx_unlock(xfer2->usb_mtx);
+
+	return;
+}
+
+void
+usbd_clearstall_callback(struct usbd_xfer *xfer)
+{
+	USBD_CHECK_STATUS(xfer);
+
+ tr_setup:
+	usbd_clear_stall_tr_setup(xfer, xfer->priv_sc);
 	return;
 
  tr_transferred:
  tr_error:
 	PRINTFN(3,("xfer=%p\n", xfer));
 
-	/* clear any stall and make sure DATA0
-	 * toggle will be used next
-	 *
-	 * NOTE: some devices reject this command,
+	/* NOTE: some devices reject this command,
 	 * so ignore a STALL
 	 */
-	xfer = xfer->priv_sc;
+	usbd_clear_stall_tr_transferred(xfer, xfer->priv_sc);
 
-	xfer->pipe->clearstall = 0;
-	xfer->pipe->toggle_next = 0;
-
-	usbd_start_hardware(xfer);
+	usbd_start_hardware(xfer->priv_sc);
 	return;
 }
 


More information about the p4-projects mailing list