From owner-p4-projects@FreeBSD.ORG Mon Jun 5 12:50:01 2006 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 1728C16A61A; Mon, 5 Jun 2006 12:50:01 +0000 (UTC) X-Original-To: perforce@FreeBSD.org Delivered-To: perforce@FreeBSD.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id B51BA16A611 for ; Mon, 5 Jun 2006 12:49:58 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id 6750743D55 for ; Mon, 5 Jun 2006 12:49:58 +0000 (GMT) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id k55CmEMx083772 for ; Mon, 5 Jun 2006 12:48:14 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id k55CmEZT083769 for perforce@freebsd.org; Mon, 5 Jun 2006 12:48:14 GMT (envelope-from hselasky@FreeBSD.org) Date: Mon, 5 Jun 2006 12:48:14 GMT Message-Id: <200606051248.k55CmEZT083769@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 98544 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 05 Jun 2006 12:50:07 -0000 http://perforce.freebsd.org/chv.cgi?CH=98544 Change 98544 by hselasky@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@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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 #include #include +#include __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; }