summaryrefslogtreecommitdiffstats
path: root/sys/legacy/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/legacy/dev')
-rw-r--r--sys/legacy/dev/usb/FILES52
-rw-r--r--sys/legacy/dev/usb/dsbr100io.h39
-rw-r--r--sys/legacy/dev/usb/ehci.c3932
-rw-r--r--sys/legacy/dev/usb/ehci_ddb.c255
-rw-r--r--sys/legacy/dev/usb/ehci_ixp4xx.c360
-rw-r--r--sys/legacy/dev/usb/ehci_mbus.c396
-rw-r--r--sys/legacy/dev/usb/ehci_pci.c636
-rw-r--r--sys/legacy/dev/usb/ehcireg.h344
-rw-r--r--sys/legacy/dev/usb/ehcivar.h278
-rw-r--r--sys/legacy/dev/usb/hid.c469
-rw-r--r--sys/legacy/dev/usb/hid.h91
-rw-r--r--sys/legacy/dev/usb/if_aue.c1498
-rw-r--r--sys/legacy/dev/usb/if_auereg.h294
-rw-r--r--sys/legacy/dev/usb/if_axe.c1428
-rw-r--r--sys/legacy/dev/usb/if_axereg.h254
-rw-r--r--sys/legacy/dev/usb/if_cdce.c771
-rw-r--r--sys/legacy/dev/usb/if_cdcereg.h79
-rw-r--r--sys/legacy/dev/usb/if_cue.c1074
-rw-r--r--sys/legacy/dev/usb/if_cuereg.h168
-rw-r--r--sys/legacy/dev/usb/if_kue.c1024
-rw-r--r--sys/legacy/dev/usb/if_kuereg.h161
-rw-r--r--sys/legacy/dev/usb/if_rue.c1393
-rw-r--r--sys/legacy/dev/usb/if_ruereg.h226
-rw-r--r--sys/legacy/dev/usb/if_rum.c2560
-rw-r--r--sys/legacy/dev/usb/if_rumreg.h235
-rw-r--r--sys/legacy/dev/usb/if_rumvar.h161
-rw-r--r--sys/legacy/dev/usb/if_udav.c1962
-rw-r--r--sys/legacy/dev/usb/if_udavreg.h210
-rw-r--r--sys/legacy/dev/usb/if_upgt.c2375
-rw-r--r--sys/legacy/dev/usb/if_upgtvar.h462
-rw-r--r--sys/legacy/dev/usb/if_ural.c2505
-rw-r--r--sys/legacy/dev/usb/if_uralreg.h210
-rw-r--r--sys/legacy/dev/usb/if_uralvar.h157
-rw-r--r--sys/legacy/dev/usb/if_urtw.c3324
-rw-r--r--sys/legacy/dev/usb/if_urtwreg.h256
-rw-r--r--sys/legacy/dev/usb/if_urtwvar.h151
-rw-r--r--sys/legacy/dev/usb/if_zyd.c3126
-rw-r--r--sys/legacy/dev/usb/if_zydfw.h1144
-rw-r--r--sys/legacy/dev/usb/if_zydreg.h1322
-rw-r--r--sys/legacy/dev/usb/kue_fw.h685
-rw-r--r--sys/legacy/dev/usb/ohci.c3638
-rw-r--r--sys/legacy/dev/usb/ohci_pci.c411
-rw-r--r--sys/legacy/dev/usb/ohcireg.h250
-rw-r--r--sys/legacy/dev/usb/ohcivar.h164
-rw-r--r--sys/legacy/dev/usb/rio500_usb.h48
-rw-r--r--sys/legacy/dev/usb/rt2573_ucode.h213
-rw-r--r--sys/legacy/dev/usb/sl811hs.c1654
-rw-r--r--sys/legacy/dev/usb/sl811hsreg.h126
-rw-r--r--sys/legacy/dev/usb/sl811hsvar.h107
-rw-r--r--sys/legacy/dev/usb/slhci_pccard.c206
-rw-r--r--sys/legacy/dev/usb/u3g.c799
-rw-r--r--sys/legacy/dev/usb/uark.c339
-rw-r--r--sys/legacy/dev/usb/ubsa.c742
-rw-r--r--sys/legacy/dev/usb/ubser.c882
-rw-r--r--sys/legacy/dev/usb/ubser.h43
-rw-r--r--sys/legacy/dev/usb/uchcom.c1036
-rw-r--r--sys/legacy/dev/usb/ucom.c829
-rw-r--r--sys/legacy/dev/usb/ucomvar.h169
-rw-r--r--sys/legacy/dev/usb/ucycom.c543
-rw-r--r--sys/legacy/dev/usb/udbp.c860
-rw-r--r--sys/legacy/dev/usb/udbp.h80
-rw-r--r--sys/legacy/dev/usb/ufm.c376
-rw-r--r--sys/legacy/dev/usb/ufoma.c1192
-rw-r--r--sys/legacy/dev/usb/uftdi.c793
-rw-r--r--sys/legacy/dev/usb/uftdireg.h338
-rw-r--r--sys/legacy/dev/usb/ugen.c1590
-rw-r--r--sys/legacy/dev/usb/ugraphire_rdesc.h176
-rw-r--r--sys/legacy/dev/usb/uhci.c3704
-rw-r--r--sys/legacy/dev/usb/uhci_pci.c524
-rw-r--r--sys/legacy/dev/usb/uhcireg.h193
-rw-r--r--sys/legacy/dev/usb/uhcivar.h206
-rw-r--r--sys/legacy/dev/usb/uhid.c755
-rw-r--r--sys/legacy/dev/usb/uhub.c703
-rw-r--r--sys/legacy/dev/usb/uipaq.c822
-rw-r--r--sys/legacy/dev/usb/ukbd.c1538
-rw-r--r--sys/legacy/dev/usb/ulpt.c815
-rw-r--r--sys/legacy/dev/usb/umass.c3611
-rw-r--r--sys/legacy/dev/usb/umct.c511
-rw-r--r--sys/legacy/dev/usb/umodem.c821
-rw-r--r--sys/legacy/dev/usb/ums.c972
-rw-r--r--sys/legacy/dev/usb/uplcom.c990
-rw-r--r--sys/legacy/dev/usb/urio.c518
-rw-r--r--sys/legacy/dev/usb/usb.c939
-rw-r--r--sys/legacy/dev/usb/usb.h708
-rw-r--r--sys/legacy/dev/usb/usb_ethersubr.c283
-rw-r--r--sys/legacy/dev/usb/usb_ethersubr.h91
-rw-r--r--sys/legacy/dev/usb/usb_if.m42
-rw-r--r--sys/legacy/dev/usb/usb_mem.c297
-rw-r--r--sys/legacy/dev/usb/usb_mem.h58
-rw-r--r--sys/legacy/dev/usb/usb_port.h200
-rw-r--r--sys/legacy/dev/usb/usb_quirks.c153
-rw-r--r--sys/legacy/dev/usb/usb_quirks.h64
-rw-r--r--sys/legacy/dev/usb/usb_subr.c1388
-rw-r--r--sys/legacy/dev/usb/usbcdc.h188
-rw-r--r--sys/legacy/dev/usb/usbdevs2527
-rw-r--r--sys/legacy/dev/usb/usbdi.c1383
-rw-r--r--sys/legacy/dev/usb/usbdi.h289
-rw-r--r--sys/legacy/dev/usb/usbdi_util.c539
-rw-r--r--sys/legacy/dev/usb/usbdi_util.h98
-rw-r--r--sys/legacy/dev/usb/usbdivar.h322
-rw-r--r--sys/legacy/dev/usb/usbhid.h185
-rw-r--r--sys/legacy/dev/usb/uscanner.c723
-rw-r--r--sys/legacy/dev/usb/uslcom.c419
-rw-r--r--sys/legacy/dev/usb/uvisor.c639
-rw-r--r--sys/legacy/dev/usb/uvscom.c906
-rw-r--r--sys/legacy/dev/usb/uxb360gp_rdesc.h124
106 files changed, 82919 insertions, 0 deletions
diff --git a/sys/legacy/dev/usb/FILES b/sys/legacy/dev/usb/FILES
new file mode 100644
index 0000000..5a77cbd
--- /dev/null
+++ b/sys/legacy/dev/usb/FILES
@@ -0,0 +1,52 @@
+$FreeBSD$
+
+A small roadmap of the USB files:
+
+FILES this file
+dsbr100io.h API for ufm.c
+ehci.c Host controller driver for EHCI
+ehcireg.h Hardware definitions for EHCI
+ehcivar.h API for ehci.c
+hid.c subroutines to parse and access HID data
+hid.h API for hid.c
+if_aue.c USB Pegasus Ethernet driver
+if_auereg.h and definitions for it
+if_axe.c USB ASIX Electronics Ethernet driver
+if_axereg.h and definitions for it
+if_cue.c USB CATC Ethernet driver
+if_cuereg.h and definitions for it
+if_kue.c USB Kawasaki Ethernet driver
+if_kuereg.h and definitions for it
+ohci.c Host controller driver for OHCI
+ohcireg.h Hardware definitions for OHCI
+ohcivar.h API for ohci.c
+ufm.c USB fm radio driver
+[Merged] ugen.c generic driver that can handle access to any USB device
+uhci.c Host controller driver for UHCI
+uhcireg.h Hardware definitions for UHCI
+uhcivar.h API for uhci.c
+uhid.c USB HID class driver
+uhub.c USB hub driver
+ukbd.c USB keyboard driver
+ulpt.c USB printer class driver
+umass.c USB mass storage driver
+umodem.c USB modem (CDC ACM) driver
+ums.c USB mouse driver
+urio.c USB Diamond Rio500 driver
+usb.c usb (bus) device driver
+usb.h general USB defines
+usb_mem.c memory allocation for DMAable memory
+usb_mem.h API for usb_mem.c
+usb_port.h compatibility defines for different OSs
+usb_quirks.c table of non-conforming USB devices and their problems
+usb_quirks.h API for usb_quirks.c
+usb_subr.c various subroutines used by USB code
+usbcdc.h USB CDC class definitions
+usbdevs data base of known device
+usbdi.c implementation of the USBDI API, which all drivers use
+usbdi.h API for usbdi.c
+usbdi_util.c utilities built on top of usbdi.h
+usbdi_util.h API for usbdi_util.c
+usbdivar.h internal defines and structures for usbdi.c
+uscanner.c minimal USB scanner driver
+usbhid.h USB HID class definitions
diff --git a/sys/legacy/dev/usb/dsbr100io.h b/sys/legacy/dev/usb/dsbr100io.h
new file mode 100644
index 0000000..f1c8a97
--- /dev/null
+++ b/sys/legacy/dev/usb/dsbr100io.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2001 M. Warner Losh
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+/* $FreeBSD$ */
+
+#include <sys/ioccom.h>
+
+#define FM_SET_FREQ _IOWR('U', 200, int)
+#define FM_GET_FREQ _IOWR('U', 201, int)
+#define FM_START _IOWR('U', 202, int)
+#define FM_STOP _IOWR('U', 203, int)
+#define FM_GET_STAT _IOWR('U', 204, int)
diff --git a/sys/legacy/dev/usb/ehci.c b/sys/legacy/dev/usb/ehci.c
new file mode 100644
index 0000000..c159600
--- /dev/null
+++ b/sys/legacy/dev/usb/ehci.c
@@ -0,0 +1,3932 @@
+/* $NetBSD: ehci.c,v 1.91 2005/02/27 00:27:51 perry Exp $ */
+
+/*-
+ * Copyright (c) 2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) and by Charles M. Hannum.
+ *
+ * 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.
+ */
+
+/*
+ * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
+ *
+ * The EHCI 1.0 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/docs/usb_20.zip
+ *
+ */
+
+/*
+ * TODO:
+ * 1) The EHCI driver lacks support for isochronous transfers, so
+ * devices using them don't work.
+ *
+ * 2) Interrupt transfer scheduling does not manage the time available
+ * in each frame, so it is possible for the transfers to overrun
+ * the end of the frame.
+ *
+ * 3) Command failures are not recovered correctly.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/lockmgr.h>
+#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__)
+#include <machine/cpu.h>
+#endif
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ehcireg.h>
+#include <dev/usb/ehcivar.h>
+
+#define delay(d) DELAY(d)
+
+#ifdef USB_DEBUG
+#define EHCI_DEBUG USB_DEBUG
+#define DPRINTF(x) do { if (ehcidebug) printf x; } while (0)
+#define DPRINTFN(n,x) do { if (ehcidebug>(n)) printf x; } while (0)
+int ehcidebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci");
+SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW,
+ &ehcidebug, 0, "ehci debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct ehci_pipe {
+ struct usbd_pipe pipe;
+
+ ehci_soft_qh_t *sqh;
+ union {
+ ehci_soft_qtd_t *qtd;
+ /* ehci_soft_itd_t *itd; */
+ } tail;
+ union {
+ /* Control pipe */
+ struct {
+ usb_dma_t reqdma;
+ u_int length;
+ /*ehci_soft_qtd_t *setup, *data, *stat;*/
+ } ctl;
+ /* Interrupt pipe */
+ struct {
+ u_int length;
+ } intr;
+ /* Bulk pipe */
+ struct {
+ u_int length;
+ } bulk;
+ /* Iso pipe */
+ struct {
+ u_int next_frame;
+ u_int cur_xfers;
+ } isoc;
+ } u;
+};
+
+static usbd_status ehci_open(usbd_pipe_handle);
+static void ehci_poll(struct usbd_bus *);
+static void ehci_softintr(void *);
+static int ehci_intr1(ehci_softc_t *);
+static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle);
+static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *);
+static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *);
+static void ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *);
+static void ehci_idone(struct ehci_xfer *);
+static void ehci_timeout(void *);
+static void ehci_timeout_task(void *);
+static void ehci_intrlist_timeout(void *);
+
+static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
+static void ehci_freem(struct usbd_bus *, usb_dma_t *);
+
+static usbd_xfer_handle ehci_allocx(struct usbd_bus *);
+static void ehci_freex(struct usbd_bus *, usbd_xfer_handle);
+
+static usbd_status ehci_root_ctrl_transfer(usbd_xfer_handle);
+static usbd_status ehci_root_ctrl_start(usbd_xfer_handle);
+static void ehci_root_ctrl_abort(usbd_xfer_handle);
+static void ehci_root_ctrl_close(usbd_pipe_handle);
+static void ehci_root_ctrl_done(usbd_xfer_handle);
+
+static usbd_status ehci_root_intr_transfer(usbd_xfer_handle);
+static usbd_status ehci_root_intr_start(usbd_xfer_handle);
+static void ehci_root_intr_abort(usbd_xfer_handle);
+static void ehci_root_intr_close(usbd_pipe_handle);
+static void ehci_root_intr_done(usbd_xfer_handle);
+
+static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle);
+static usbd_status ehci_device_ctrl_start(usbd_xfer_handle);
+static void ehci_device_ctrl_abort(usbd_xfer_handle);
+static void ehci_device_ctrl_close(usbd_pipe_handle);
+static void ehci_device_ctrl_done(usbd_xfer_handle);
+
+static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle);
+static usbd_status ehci_device_bulk_start(usbd_xfer_handle);
+static void ehci_device_bulk_abort(usbd_xfer_handle);
+static void ehci_device_bulk_close(usbd_pipe_handle);
+static void ehci_device_bulk_done(usbd_xfer_handle);
+
+static usbd_status ehci_device_intr_transfer(usbd_xfer_handle);
+static usbd_status ehci_device_intr_start(usbd_xfer_handle);
+static void ehci_device_intr_abort(usbd_xfer_handle);
+static void ehci_device_intr_close(usbd_pipe_handle);
+static void ehci_device_intr_done(usbd_xfer_handle);
+
+static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle);
+static usbd_status ehci_device_isoc_start(usbd_xfer_handle);
+static void ehci_device_isoc_abort(usbd_xfer_handle);
+static void ehci_device_isoc_close(usbd_pipe_handle);
+static void ehci_device_isoc_done(usbd_xfer_handle);
+
+static void ehci_device_clear_toggle(usbd_pipe_handle pipe);
+static void ehci_noop(usbd_pipe_handle pipe);
+
+static int ehci_str(usb_string_descriptor_t *, int, char *);
+static void ehci_pcd(ehci_softc_t *, usbd_xfer_handle);
+static void ehci_disown(ehci_softc_t *, int, int);
+
+static ehci_soft_qh_t *ehci_alloc_sqh(ehci_softc_t *);
+static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *);
+
+static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *);
+static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *);
+static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *,
+ ehci_softc_t *, int, int, usbd_xfer_handle,
+ ehci_soft_qtd_t *, ehci_soft_qtd_t *,
+ ehci_soft_qtd_t **, ehci_soft_qtd_t **);
+static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qh_t *,
+ ehci_soft_qtd_t *, ehci_soft_qtd_t *);
+
+static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *);
+static void ehci_free_itd(ehci_softc_t *, ehci_soft_itd_t *);
+static void ehci_rem_free_itd_chain(ehci_softc_t *,
+ struct ehci_xfer *);
+static void ehci_abort_isoc_xfer(usbd_xfer_handle, usbd_status);
+
+static usbd_status ehci_device_request(usbd_xfer_handle xfer);
+
+static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *,
+ int ival);
+
+static void ehci_add_qh(ehci_softc_t *, ehci_soft_qh_t *,
+ ehci_soft_qh_t *);
+static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *,
+ ehci_soft_qh_t *);
+static void ehci_activate_qh(ehci_softc_t *sc, ehci_soft_qh_t *,
+ ehci_soft_qtd_t *);
+static void ehci_sync_hc(ehci_softc_t *);
+
+static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *);
+static void ehci_abort_xfer(usbd_xfer_handle, usbd_status);
+
+ehci_softc_t *theehci;
+
+#define EHCI_NULL(sc) htohc32(sc, EHCI_LINK_TERMINATE)
+
+#define EHCI_INTR_ENDPT 1
+
+#define ehci_add_intr_list(sc, ex) \
+ LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ex), inext);
+#define ehci_del_intr_list(ex) \
+ do { \
+ LIST_REMOVE((ex), inext); \
+ (ex)->inext.le_prev = NULL; \
+ } while (0)
+#define ehci_active_intr_list(ex) ((ex)->inext.le_prev != NULL)
+
+static struct usbd_bus_methods ehci_bus_methods = {
+ ehci_open,
+ ehci_softintr,
+ ehci_poll,
+ ehci_allocm,
+ ehci_freem,
+ ehci_allocx,
+ ehci_freex,
+};
+
+static struct usbd_pipe_methods ehci_root_ctrl_methods = {
+ ehci_root_ctrl_transfer,
+ ehci_root_ctrl_start,
+ ehci_root_ctrl_abort,
+ ehci_root_ctrl_close,
+ ehci_noop,
+ ehci_root_ctrl_done,
+};
+
+static struct usbd_pipe_methods ehci_root_intr_methods = {
+ ehci_root_intr_transfer,
+ ehci_root_intr_start,
+ ehci_root_intr_abort,
+ ehci_root_intr_close,
+ ehci_noop,
+ ehci_root_intr_done,
+};
+
+static struct usbd_pipe_methods ehci_device_ctrl_methods = {
+ ehci_device_ctrl_transfer,
+ ehci_device_ctrl_start,
+ ehci_device_ctrl_abort,
+ ehci_device_ctrl_close,
+ ehci_noop,
+ ehci_device_ctrl_done,
+};
+
+static struct usbd_pipe_methods ehci_device_intr_methods = {
+ ehci_device_intr_transfer,
+ ehci_device_intr_start,
+ ehci_device_intr_abort,
+ ehci_device_intr_close,
+ ehci_device_clear_toggle,
+ ehci_device_intr_done,
+};
+
+static struct usbd_pipe_methods ehci_device_bulk_methods = {
+ ehci_device_bulk_transfer,
+ ehci_device_bulk_start,
+ ehci_device_bulk_abort,
+ ehci_device_bulk_close,
+ ehci_device_clear_toggle,
+ ehci_device_bulk_done,
+};
+
+static struct usbd_pipe_methods ehci_device_isoc_methods = {
+ ehci_device_isoc_transfer,
+ ehci_device_isoc_start,
+ ehci_device_isoc_abort,
+ ehci_device_isoc_close,
+ ehci_noop,
+ ehci_device_isoc_done,
+};
+
+usbd_status
+ehci_reset(ehci_softc_t *sc)
+{
+ u_int32_t hcr;
+ u_int i;
+
+ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
+ for (i = 0; i < 100; i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET;
+ if (!hcr) {
+ if (sc->sc_flags & (EHCI_SCFLG_SETMODE | EHCI_SCFLG_BIGEMMIO)) {
+ /*
+ * Force USBMODE as requested. Controllers
+ * may have multiple operating modes.
+ */
+ uint32_t usbmode = EOREAD4(sc, EHCI_USBMODE);
+ if (sc->sc_flags & EHCI_SCFLG_SETMODE) {
+ usbmode = (usbmode &~ EHCI_UM_CM) | EHCI_UM_CM_HOST;
+ device_printf(sc->sc_bus.bdev,
+ "set host controller mode\n");
+ }
+ if (sc->sc_flags & EHCI_SCFLG_BIGEMMIO) {
+ usbmode = (usbmode &~ EHCI_UM_ES) | EHCI_UM_ES_BE;
+ device_printf(sc->sc_bus.bdev,
+ "set big-endian mode\n");
+ }
+ EOWRITE4(sc, EHCI_USBMODE, usbmode);
+ }
+ return (USBD_NORMAL_COMPLETION);
+ }
+ }
+ printf("%s: reset timeout\n", device_get_nameunit(sc->sc_bus.bdev));
+ return (USBD_IOERROR);
+}
+
+static usbd_status
+ehci_hcreset(ehci_softc_t *sc)
+{
+ u_int32_t hcr;
+ u_int i;
+
+ EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
+ for (i = 0; i < 100; i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (hcr)
+ break;
+ }
+ if (!hcr)
+ /*
+ * Fall through and try reset anyway even though
+ * Table 2-9 in the EHCI spec says this will result
+ * in undefined behavior.
+ */
+ device_printf(sc->sc_bus.bdev, "stop timeout\n");
+
+ return ehci_reset(sc);
+}
+
+usbd_status
+ehci_init(ehci_softc_t *sc)
+{
+ u_int32_t version, sparams, cparams, hcr;
+ u_int i;
+ usbd_status err;
+ ehci_soft_qh_t *sqh;
+ u_int ncomp;
+ int lev;
+
+ DPRINTF(("ehci_init: start\n"));
+#ifdef EHCI_DEBUG
+ theehci = sc;
+#endif
+
+ /* NB: must handle byte-order manually before ehci_hcreset */
+
+ sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH);
+
+ version = EREAD2(sc, EHCI_HCIVERSION);
+ device_printf(sc->sc_bus.bdev, "EHCI version %x.%x\n",
+ version >> 8, version & 0xff);
+
+ sparams = EREAD4(sc, EHCI_HCSPARAMS);
+ DPRINTF(("ehci_init: sparams=0x%x\n", sparams));
+ sc->sc_npcomp = EHCI_HCS_N_PCC(sparams);
+ ncomp = EHCI_HCS_N_CC(sparams);
+ if (ncomp != sc->sc_ncomp) {
+ printf("%s: wrong number of companions (%d != %d)\n",
+ device_get_nameunit(sc->sc_bus.bdev),
+ ncomp, sc->sc_ncomp);
+ if (ncomp < sc->sc_ncomp)
+ sc->sc_ncomp = ncomp;
+ }
+ if (sc->sc_ncomp > 0) {
+ printf("%s: companion controller%s, %d port%s each:",
+ device_get_nameunit(sc->sc_bus.bdev), sc->sc_ncomp!=1 ? "s" : "",
+ EHCI_HCS_N_PCC(sparams),
+ EHCI_HCS_N_PCC(sparams)!=1 ? "s" : "");
+ for (i = 0; i < sc->sc_ncomp; i++)
+ printf(" %s", device_get_nameunit(sc->sc_comps[i]->bdev));
+ printf("\n");
+ }
+ sc->sc_noport = EHCI_HCS_N_PORTS(sparams);
+ cparams = EREAD4(sc, EHCI_HCCPARAMS);
+ DPRINTF(("ehci_init: cparams=0x%x\n", cparams));
+
+ if (EHCI_HCC_64BIT(cparams)) {
+ /* MUST clear segment register if 64 bit capable. */
+ EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+ }
+
+ sc->sc_bus.usbrev = USBREV_2_0;
+
+ /* Reset the controller */
+ DPRINTF(("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)));
+ err = ehci_hcreset(sc);
+ if (err != USBD_NORMAL_COMPLETION)
+ return (err);
+
+ /* frame list size at default, read back what we got and use that */
+ switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) {
+ case 0: sc->sc_flsize = 1024; break;
+ case 1: sc->sc_flsize = 512; break;
+ case 2: sc->sc_flsize = 256; break;
+ case 3: return (USBD_IOERROR);
+ }
+ err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t),
+ EHCI_FLALIGN_ALIGN, &sc->sc_fldma);
+ if (err)
+ return (err);
+ DPRINTF(("%s: flsize=%d\n", device_get_nameunit(sc->sc_bus.bdev),sc->sc_flsize));
+ sc->sc_flist = KERNADDR(&sc->sc_fldma, 0);
+
+ for (i = 0; i < sc->sc_flsize; i++) {
+ sc->sc_flist[i] = EHCI_NULL(sc);
+ }
+
+ EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
+
+ sc->sc_softitds = malloc(sc->sc_flsize * sizeof(ehci_soft_itd_t *),
+ M_USB, M_NOWAIT | M_ZERO);
+ if (sc->sc_softitds == NULL)
+ return (ENOMEM);
+ LIST_INIT(&sc->sc_freeitds);
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &ehci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct ehci_pipe);
+
+ sc->sc_eintrs = EHCI_NORMAL_INTRS;
+
+ /*
+ * Allocate the interrupt dummy QHs. These are arranged to give
+ * poll intervals that are powers of 2 times 1ms.
+ */
+ for (i = 0; i < EHCI_INTRQHS; i++) {
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ sc->sc_islots[i].sqh = sqh;
+ }
+ lev = 0;
+ for (i = 0; i < EHCI_INTRQHS; i++) {
+ if (i == EHCI_IQHIDX(lev + 1, 0))
+ lev++;
+ sqh = sc->sc_islots[i].sqh;
+ if (i == 0) {
+ /* The last (1ms) QH terminates. */
+ sqh->qh.qh_link = EHCI_NULL(sc);
+ sqh->next = NULL;
+ } else {
+ /* Otherwise the next QH has half the poll interval */
+ sqh->next =
+ sc->sc_islots[EHCI_IQHIDX(lev - 1, i + 1)].sqh;
+ sqh->qh.qh_link = htohc32(sc, sqh->next->physaddr |
+ EHCI_LINK_QH);
+ }
+ sqh->qh.qh_endp = htohc32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH));
+ sqh->qh.qh_endphub = htohc32(sc, EHCI_QH_SET_MULT(1));
+ sqh->qh.qh_curqtd = EHCI_NULL(sc);
+ sqh->qh.qh_qtd.qtd_next = EHCI_NULL(sc);
+ sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL(sc);
+ sqh->qh.qh_qtd.qtd_status = htohc32(sc, EHCI_QTD_HALTED);
+ }
+ /* Point the frame list at the last level (128ms). */
+ for (i = 0; i < sc->sc_flsize; i++) {
+ sc->sc_flist[i] = htohc32(sc, EHCI_LINK_QH |
+ sc->sc_islots[EHCI_IQHIDX(EHCI_IPOLLRATES - 1, i)].sqh->physaddr);
+ }
+
+ /* Allocate dummy QH that starts the async list. */
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ /* Fill the QH */
+ sqh->qh.qh_endp =
+ htohc32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL);
+ sqh->qh.qh_link =
+ htohc32(sc, sqh->physaddr | EHCI_LINK_QH);
+ sqh->qh.qh_curqtd = EHCI_NULL(sc);
+ sqh->prev = sqh; /*It's a circular list.. */
+ sqh->next = sqh;
+ /* Fill the overlay qTD */
+ sqh->qh.qh_qtd.qtd_next = EHCI_NULL(sc);
+ sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL(sc);
+ sqh->qh.qh_qtd.qtd_status = htohc32(sc, 0);
+#ifdef EHCI_DEBUG
+ if (ehcidebug) {
+ ehci_dump_sqh(sc, sqh);
+ }
+#endif
+
+ /* Point to async list */
+ sc->sc_async_head = sqh;
+ EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH);
+
+ callout_init(&sc->sc_tmo_intrlist, 0);
+
+ lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0);
+
+ /* Enable interrupts */
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ /* Turn on controller */
+ EOWRITE4(sc, EHCI_USBCMD,
+ EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */
+ (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) |
+ EHCI_CMD_ASE |
+ EHCI_CMD_PSE |
+ EHCI_CMD_RS);
+
+ /* Take over port ownership */
+ EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF);
+
+ for (i = 0; i < 100; i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (!hcr)
+ break;
+ }
+ if (hcr) {
+ printf("%s: run timeout\n", device_get_nameunit(sc->sc_bus.bdev));
+ return (USBD_IOERROR);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+
+#if 0
+ bad2:
+ ehci_free_sqh(sc, sc->sc_async_head);
+#endif
+ bad1:
+ usb_freemem(&sc->sc_bus, &sc->sc_fldma);
+ return (err);
+}
+
+int
+ehci_intr(void *v)
+{
+ ehci_softc_t *sc = v;
+
+ if (sc == NULL || sc->sc_dying)
+ return (0);
+
+ /* If we get an interrupt while polling, then just ignore it. */
+ if (sc->sc_bus.use_polling) {
+ u_int32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+
+ if (intrs)
+ EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */
+#ifdef DIAGNOSTIC
+ DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n"));
+#endif
+ return (0);
+ }
+
+ return (ehci_intr1(sc));
+}
+
+static int
+ehci_intr1(ehci_softc_t *sc)
+{
+ u_int32_t intrs, eintrs;
+
+ DPRINTFN(20,("ehci_intr1: enter\n"));
+
+ /* In case the interrupt occurs before initialization has completed. */
+ if (sc == NULL) {
+#ifdef DIAGNOSTIC
+ printf("ehci_intr1: sc == NULL\n");
+#endif
+ return (0);
+ }
+
+ intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+ if (!intrs)
+ return (0);
+
+ eintrs = intrs & sc->sc_eintrs;
+ DPRINTFN(7, ("ehci_intr1: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n",
+ sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS),
+ (u_int)eintrs));
+ if (!eintrs)
+ return (0);
+
+ EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */
+ sc->sc_bus.intr_context++;
+ sc->sc_bus.no_intrs++;
+ if (eintrs & EHCI_STS_IAA) {
+ DPRINTF(("ehci_intr1: door bell\n"));
+ wakeup(&sc->sc_async_head);
+ eintrs &= ~EHCI_STS_IAA;
+ }
+ if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) {
+ DPRINTFN(5,("ehci_intr1: %s %s\n",
+ eintrs & EHCI_STS_INT ? "INT" : "",
+ eintrs & EHCI_STS_ERRINT ? "ERRINT" : ""));
+ usb_schedsoftintr(&sc->sc_bus);
+ eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT);
+ }
+ if (eintrs & EHCI_STS_HSE) {
+ printf("%s: unrecoverable error, controller halted\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ /* XXX what else */
+ }
+ if (eintrs & EHCI_STS_PCD) {
+ ehci_pcd(sc, sc->sc_intrxfer);
+ eintrs &= ~EHCI_STS_PCD;
+ }
+
+ sc->sc_bus.intr_context--;
+
+ if (eintrs != 0) {
+ /* Block unprocessed interrupts. */
+ sc->sc_eintrs &= ~eintrs;
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+ printf("%s: blocking intrs 0x%x\n",
+ device_get_nameunit(sc->sc_bus.bdev), eintrs);
+ }
+
+ return (1);
+}
+
+/*
+ * XXX write back xfer data for architectures with a write-back
+ * data cache; this is a hack because usb is mis-architected
+ * in blindly mixing bus_dma w/ PIO.
+ */
+static __inline void
+hacksync(usbd_xfer_handle xfer)
+{
+ bus_dma_tag_t tag;
+ struct usb_dma_mapping *dmap;
+
+ if (xfer->length == 0)
+ return;
+ tag = xfer->pipe->device->bus->buffer_dmatag;
+ dmap = &xfer->dmamap;
+ bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREWRITE);
+}
+
+void
+ehci_pcd(ehci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe;
+ u_char *p;
+ int i, m;
+
+ if (xfer == NULL) {
+ /* Just ignore the change. */
+ return;
+ }
+
+ pipe = xfer->pipe;
+
+ p = xfer->buffer;
+ m = min(sc->sc_noport, xfer->length * 8 - 1);
+ memset(p, 0, xfer->length);
+ for (i = 1; i <= m; i++) {
+ /* Pick out CHANGE bits from the status reg. */
+ if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR)
+ p[i/8] |= 1 << (i%8);
+ }
+ DPRINTF(("ehci_pcd: change=0x%02x\n", *p));
+ xfer->actlen = xfer->length;
+ xfer->status = USBD_NORMAL_COMPLETION;
+
+ hacksync(xfer); /* XXX to compensate for usb_transfer_complete */
+ usb_transfer_complete(xfer);
+}
+
+void
+ehci_softintr(void *v)
+{
+ ehci_softc_t *sc = v;
+ struct ehci_xfer *ex, *nextex;
+
+ DPRINTFN(10,("%s: ehci_softintr (%d)\n", device_get_nameunit(sc->sc_bus.bdev),
+ sc->sc_bus.intr_context));
+
+ sc->sc_bus.intr_context++;
+
+ /*
+ * The only explanation I can think of for why EHCI is as brain dead
+ * as UHCI interrupt-wise is that Intel was involved in both.
+ * An interrupt just tells us that something is done, we have no
+ * clue what, so we need to scan through all active transfers. :-(
+ */
+ for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = nextex) {
+ nextex = LIST_NEXT(ex, inext);
+ ehci_check_intr(sc, ex);
+ }
+
+ /* Schedule a callout to catch any dropped transactions. */
+ if ((sc->sc_flags & EHCI_SCFLG_LOSTINTRBUG) &&
+ !LIST_EMPTY(&sc->sc_intrhead))
+ callout_reset(&sc->sc_tmo_intrlist, hz / 5,
+ ehci_intrlist_timeout, sc);
+
+#ifdef USB_USE_SOFTINTR
+ if (sc->sc_softwake) {
+ sc->sc_softwake = 0;
+ wakeup(&sc->sc_softwake);
+ }
+#endif /* USB_USE_SOFTINTR */
+
+ sc->sc_bus.intr_context--;
+}
+
+/* Check for an interrupt. */
+void
+ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+ int attr;
+
+ DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex));
+
+ attr = ex->xfer.pipe->endpoint->edesc->bmAttributes;
+ if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS)
+ ehci_check_itd_intr(sc, ex);
+ else
+ ehci_check_qh_intr(sc, ex);
+}
+
+void
+ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+ ehci_soft_qtd_t *sqtd, *lsqtd;
+ u_int32_t status;
+
+ if (ex->sqtdstart == NULL) {
+ printf("ehci_check_qh_intr: not valid sqtd\n");
+ return;
+ }
+ lsqtd = ex->sqtdend;
+#ifdef DIAGNOSTIC
+ if (lsqtd == NULL) {
+ printf("ehci_check_qh_intr: lsqtd==0\n");
+ return;
+ }
+#endif
+ /*
+ * If the last TD is still active we need to check whether there
+ * is a an error somewhere in the middle, or whether there was a
+ * short packet (SPD and not ACTIVE).
+ */
+ if (hc32toh(sc, lsqtd->qtd.qtd_status) & EHCI_QTD_ACTIVE) {
+ DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex));
+ for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) {
+ status = hc32toh(sc, sqtd->qtd.qtd_status);
+ /* If there's an active QTD the xfer isn't done. */
+ if (status & EHCI_QTD_ACTIVE)
+ break;
+ /* Any kind of error makes the xfer done. */
+ if (status & EHCI_QTD_HALTED)
+ goto done;
+ /* We want short packets, and it is short: it's done */
+ if (EHCI_QTD_GET_BYTES(status) != 0)
+ goto done;
+ }
+ DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n",
+ ex, ex->sqtdstart));
+ return;
+ }
+ done:
+ DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex));
+ callout_stop(&ex->xfer.timeout_handle);
+ usb_rem_task(ex->xfer.pipe->device, &ex->abort_task);
+ ehci_idone(ex);
+}
+
+void
+ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+ ehci_soft_itd_t *itd;
+ int i;
+
+ if (ex->itdstart == NULL) {
+ printf("ehci_check_itd_intr: not valid itd\n");
+ return;
+ }
+
+ itd = ex->itdend;
+#ifdef DIAGNOSTIC
+ if (itd == NULL) {
+ printf("ehci_check_itd_intr: itdend == 0\n");
+ return;
+ }
+#endif
+
+ /*
+ * Step 1, check no active transfers in last itd, meaning we're finished
+ */
+ for (i = 0; i < 8; i++) {
+ if (hc32toh(sc, itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE)
+ break;
+ }
+
+ if (i == 8) {
+ goto done; /* All 8 descriptors inactive, it's done */
+ }
+
+ /*
+ * Step 2, check for errors in status bits, throughout chain...
+ */
+
+ DPRINTFN(12, ("ehci_check_itd_intr: active ex=%p\n", ex));
+
+ for (itd = ex->itdstart; itd != ex->itdend; itd = itd->xfer_next) {
+ for (i = 0; i < 8; i++) {
+ if (hc32toh(sc, itd->itd.itd_ctl[i]) & (EHCI_ITD_BUF_ERR |
+ EHCI_ITD_BABBLE | EHCI_ITD_ERROR))
+ break;
+ }
+ if (i != 8) { /* Error in one of the itds */
+ goto done;
+ }
+ } /* itd search loop */
+
+ DPRINTFN(12, ("ehci_check_itd_intr: ex %p itd %p still active\n", ex,
+ ex->itdstart));
+ return;
+done:
+ DPRINTFN(12, ("ehci_check_itd_intr: ex=%p done\n", ex));
+ callout_stop(&ex->xfer.timeout_handle);
+ usb_rem_task(ex->xfer.pipe->device, &ex->abort_task);
+ ehci_idone(ex);
+}
+
+void
+ehci_idone(struct ehci_xfer *ex)
+{
+ usbd_xfer_handle xfer = &ex->xfer;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
+ ehci_soft_qtd_t *sqtd, *lsqtd;
+ u_int32_t status = 0, nstatus = 0;
+ ehci_physaddr_t nextphys, altnextphys;
+ int actlen, cerr;
+
+ DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex));
+#ifdef DIAGNOSTIC
+ {
+ int s = splhigh();
+ if (ex->isdone) {
+ splx(s);
+#ifdef EHCI_DEBUG
+ printf("ehci_idone: ex is done!\n ");
+ ehci_dump_exfer(ex);
+#else
+ printf("ehci_idone: ex=%p is done!\n", ex);
+#endif
+ return;
+ }
+ ex->isdone = 1;
+ splx(s);
+ }
+#endif
+
+ if (xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer));
+ return;
+ }
+
+#ifdef EHCI_DEBUG
+ DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, epipe));
+ if (ehcidebug > 10)
+ ehci_dump_sqtds(sc, ex->sqtdstart);
+#endif
+
+ /*
+ * Make sure that the QH overlay qTD does not reference any
+ * of the qTDs we are about to free. This is probably only
+ * necessary if the transfer is marked as HALTED.
+ */
+ nextphys = EHCI_LINK_ADDR(hc32toh(sc, epipe->sqh->qh.qh_qtd.qtd_next));
+ altnextphys =
+ EHCI_LINK_ADDR(hc32toh(sc, epipe->sqh->qh.qh_qtd.qtd_altnext));
+ for (sqtd = ex->sqtdstart; sqtd != ex->sqtdend->nextqtd;
+ sqtd = sqtd->nextqtd) {
+ if (sqtd->physaddr == nextphys) {
+ epipe->sqh->qh.qh_qtd.qtd_next =
+ htohc32(sc, ex->sqtdend->nextqtd->physaddr);
+ DPRINTFN(4, ("ehci_idone: updated overlay next ptr\n"));
+
+ }
+ if (sqtd->physaddr == altnextphys) {
+ DPRINTFN(4,
+ ("ehci_idone: updated overlay altnext ptr\n"));
+ epipe->sqh->qh.qh_qtd.qtd_altnext =
+ htohc32(sc, ex->sqtdend->nextqtd->physaddr);
+ }
+ }
+
+ /* The transfer is done, compute actual length and status. */
+ if (UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes)
+ == UE_ISOCHRONOUS) {
+ /* Isoc transfer */
+ struct ehci_soft_itd *itd;
+ int i, nframes, len, uframes;
+
+ nframes = 0;
+ actlen = 0;
+
+ switch (xfer->pipe->endpoint->edesc->bInterval) {
+ case 0:
+ panic("ehci: isoc xfer suddenly has 0 bInterval, "
+ "invalid\n");
+ case 1:
+ uframes = 1;
+ break;
+ case 2:
+ uframes = 2;
+ break;
+ case 3:
+ uframes = 4;
+ break;
+ default:
+ uframes = 8;
+ break;
+ }
+
+ for (itd = ex->itdstart; itd != NULL; itd = itd->xfer_next) {
+ for (i = 0; i < 8; i += uframes) {
+ /* XXX - driver didn't fill in the frame full
+ * of uframes. This leads to scheduling
+ * inefficiencies, but working around
+ * this doubles complexity of tracking
+ * an xfer.
+ */
+ if (nframes >= xfer->nframes)
+ break;
+
+ status = hc32toh(sc, itd->itd.itd_ctl[i]);
+ len = EHCI_ITD_GET_LEN(status);
+ xfer->frlengths[nframes++] = len;
+ actlen += len;
+ }
+ if (nframes >= xfer->nframes)
+ break;
+ }
+ xfer->actlen = actlen;
+ xfer->status = USBD_NORMAL_COMPLETION;
+
+ goto end;
+ }
+
+ /* Continue processing xfers using queue heads */
+
+ lsqtd = ex->sqtdend;
+ actlen = 0;
+ for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd;
+ sqtd =sqtd->nextqtd) {
+ nstatus = hc32toh(sc, sqtd->qtd.qtd_status);
+ if (nstatus & EHCI_QTD_ACTIVE)
+ break;
+
+ status = nstatus;
+ /* halt is ok if descriptor is last, and complete */
+ if (sqtd == lsqtd && EHCI_QTD_GET_BYTES(status) == 0)
+ status &= ~EHCI_QTD_HALTED;
+ if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP)
+ actlen += sqtd->len - EHCI_QTD_GET_BYTES(status);
+ }
+
+ cerr = EHCI_QTD_GET_CERR(status);
+ DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, cerr=%d, "
+ "status=0x%x\n", xfer->length, actlen, cerr, status));
+ xfer->actlen = actlen;
+ if ((status & EHCI_QTD_HALTED) != 0) {
+ DPRINTFN(2,
+ ("ehci_idone: error, addr=%d, endpt=0x%02x, status %b\n",
+ xfer->pipe->device->address,
+ xfer->pipe->endpoint->edesc->bEndpointAddress,
+ status, EHCI_QTD_STATUS_BITS));
+ if ((status & EHCI_QTD_BABBLE) == 0 && cerr > 0)
+ xfer->status = USBD_STALLED;
+ else
+ xfer->status = USBD_IOERROR; /* more info XXX */
+ } else {
+ xfer->status = USBD_NORMAL_COMPLETION;
+ }
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 2) {
+ ehci_dump_sqh(sc, epipe->sqh);
+ ehci_dump_sqtds(sc, ex->sqtdstart);
+ }
+#endif
+end:
+ /* XXX transfer_complete memcpys out transfer data (for in endpoints)
+ * during this call, before methods->done is called: dma sync required
+ * beforehand?
+ */
+ usb_transfer_complete(xfer);
+ DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex));
+}
+
+/*
+ * Wait here until controller claims to have an interrupt.
+ * Then call ehci_intr and return. Use timeout to avoid waiting
+ * too long.
+ */
+void
+ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ int timo = xfer->timeout;
+ int usecs;
+ u_int32_t intrs;
+
+ xfer->status = USBD_IN_PROGRESS;
+ for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ if (sc->sc_dying)
+ break;
+ intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)) &
+ sc->sc_eintrs;
+ DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs));
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 15)
+ ehci_dump_regs(sc);
+#endif
+ if (intrs) {
+ ehci_intr1(sc);
+ if (xfer->status != USBD_IN_PROGRESS)
+ return;
+ }
+ }
+
+ /* Timeout */
+ DPRINTF(("ehci_waitintr: timeout\n"));
+ xfer->status = USBD_TIMEOUT;
+ usb_transfer_complete(xfer);
+ /* XXX should free TD */
+}
+
+void
+ehci_poll(struct usbd_bus *bus)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)bus;
+#ifdef EHCI_DEBUG
+ static int last;
+ int new;
+ new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+ if (new != last) {
+ DPRINTFN(10,("ehci_poll: intrs=0x%04x\n", new));
+ last = new;
+ }
+#endif
+
+ if (EOREAD4(sc, EHCI_USBSTS) & sc->sc_eintrs)
+ ehci_intr1(sc);
+}
+
+int
+ehci_detach(struct ehci_softc *sc, int flags)
+{
+ int rv = 0;
+
+ sc->sc_dying = 1;
+
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+ (void) ehci_hcreset(sc);
+ callout_stop(&sc->sc_tmo_intrlist);
+
+ usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
+
+ usb_freemem(&sc->sc_bus, &sc->sc_fldma);
+ /* XXX free other data structures XXX */
+
+ return (rv);
+}
+
+/*
+ * Handle suspend/resume.
+ *
+ * We need to switch to polling mode here, because this routine is
+ * called from an interrupt context. This is all right since we
+ * are almost suspended anyway.
+ */
+void
+ehci_power(int why, void *v)
+{
+ ehci_softc_t *sc = v;
+ u_int32_t cmd, hcr;
+ int s, i;
+
+#ifdef EHCI_DEBUG
+ DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
+ if (ehcidebug > 0)
+ ehci_dump_regs(sc);
+#endif
+
+ s = splhardusb();
+ switch (why) {
+ case PWR_SUSPEND:
+ case PWR_STANDBY:
+ sc->sc_bus.use_polling++;
+
+ for (i = 1; i <= sc->sc_noport; i++) {
+ cmd = EOREAD4(sc, EHCI_PORTSC(i));
+ if ((cmd & EHCI_PS_PO) == 0 &&
+ (cmd & EHCI_PS_PE) == EHCI_PS_PE)
+ EOWRITE4(sc, EHCI_PORTSC(i),
+ cmd | EHCI_PS_SUSP);
+ }
+
+ sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD);
+
+ cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE);
+ EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+ for (i = 0; i < 100; i++) {
+ hcr = EOREAD4(sc, EHCI_USBSTS) &
+ (EHCI_STS_ASS | EHCI_STS_PSS);
+ if (hcr == 0)
+ break;
+
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ if (hcr != 0) {
+ printf("%s: reset timeout\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ }
+
+ cmd &= ~EHCI_CMD_RS;
+ EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+ for (i = 0; i < 100; i++) {
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (hcr == EHCI_STS_HCH)
+ break;
+
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ if (hcr != EHCI_STS_HCH) {
+ printf("%s: config timeout\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ }
+
+ sc->sc_bus.use_polling--;
+ break;
+
+ case PWR_RESUME:
+ sc->sc_bus.use_polling++;
+
+ /* restore things in case the bios sucks */
+ EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+ EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
+ EOWRITE4(sc, EHCI_ASYNCLISTADDR,
+ sc->sc_async_head->physaddr | EHCI_LINK_QH);
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ hcr = 0;
+ for (i = 1; i <= sc->sc_noport; i++) {
+ cmd = EOREAD4(sc, EHCI_PORTSC(i));
+ if ((cmd & EHCI_PS_PO) == 0 &&
+ (cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP) {
+ EOWRITE4(sc, EHCI_PORTSC(i),
+ cmd | EHCI_PS_FPR);
+ hcr = 1;
+ }
+ }
+
+ if (hcr) {
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+
+ for (i = 1; i <= sc->sc_noport; i++) {
+ cmd = EOREAD4(sc, EHCI_PORTSC(i));
+ if ((cmd & EHCI_PS_PO) == 0 &&
+ (cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)
+ EOWRITE4(sc, EHCI_PORTSC(i),
+ cmd & ~EHCI_PS_FPR);
+ }
+ }
+
+ EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd);
+
+ for (i = 0; i < 100; i++) {
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (hcr != EHCI_STS_HCH)
+ break;
+
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ if (hcr == EHCI_STS_HCH) {
+ printf("%s: config timeout\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ }
+
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+
+ sc->sc_bus.use_polling--;
+ break;
+ case PWR_SOFTSUSPEND:
+ case PWR_SOFTSTANDBY:
+ case PWR_SOFTRESUME:
+ break;
+ }
+ splx(s);
+
+#ifdef EHCI_DEBUG
+ DPRINTF(("ehci_power: sc=%p\n", sc));
+ if (ehcidebug > 0)
+ ehci_dump_regs(sc);
+#endif
+}
+
+/*
+ * Shut down the controller when the system is going down.
+ */
+void
+ehci_shutdown(void *v)
+{
+ ehci_softc_t *sc = v;
+
+ DPRINTF(("ehci_shutdown: stopping the HC\n"));
+ (void) ehci_hcreset(sc);
+}
+
+usbd_status
+ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
+{
+ usbd_status err;
+
+ err = usb_allocmem(bus, size, 0, dma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_allocm: usb_allocmem()=%d\n", err);
+#endif
+ return (err);
+}
+
+void
+ehci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ usb_freemem(bus, dma);
+}
+
+usbd_xfer_handle
+ehci_allocx(struct usbd_bus *bus)
+{
+ struct ehci_softc *sc = (struct ehci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ xfer = STAILQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_FREE) {
+ printf("ehci_allocx: xfer=%p not free, 0x%08x\n", xfer,
+ xfer->busy_free);
+ }
+#endif
+ } else {
+ xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
+ }
+ if (xfer != NULL) {
+ memset(xfer, 0, sizeof(struct ehci_xfer));
+ usb_init_task(&EXFER(xfer)->abort_task, ehci_timeout_task,
+ xfer);
+ EXFER(xfer)->ehci_xfer_flags = 0;
+#ifdef DIAGNOSTIC
+ EXFER(xfer)->isdone = 1;
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+ return (xfer);
+}
+
+void
+ehci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct ehci_softc *sc = (struct ehci_softc *)bus;
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+ xfer->busy_free);
+ return;
+ }
+ xfer->busy_free = XFER_FREE;
+ if (!EXFER(xfer)->isdone) {
+ printf("ehci_freex: !isdone\n");
+ return;
+ }
+#endif
+ STAILQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+static void
+ehci_device_clear_toggle(usbd_pipe_handle pipe)
+{
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
+
+ DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n",
+ epipe, epipe->sqh->qh.qh_qtd.qtd_status));
+#ifdef USB_DEBUG
+ if (ehcidebug)
+ usbd_dump_pipe(pipe);
+#endif
+ KASSERT((epipe->sqh->qh.qh_qtd.qtd_status &
+ htohc32(sc, EHCI_QTD_ACTIVE)) == 0,
+ ("ehci_device_clear_toggle: queue active"));
+ epipe->sqh->qh.qh_qtd.qtd_status &= htohc32(sc, ~EHCI_QTD_TOGGLE_MASK);
+}
+
+static void
+ehci_noop(usbd_pipe_handle pipe)
+{
+}
+
+usbd_status
+ehci_open(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+ u_int8_t addr = dev->address;
+ u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ ehci_soft_qh_t *sqh;
+ usbd_status err;
+ int s;
+ int ival, speed, naks;
+ int hshubaddr, hshubport;
+
+ DPRINTFN(1, ("ehci_open: pipe=%p, xfertype=%d, addr=%d, endpt=%d (%d)\n",
+ pipe, addr, ed->bEndpointAddress, sc->sc_addr, xfertype));
+
+ if (dev->myhsport) {
+ hshubaddr = dev->myhsport->parent->address;
+ hshubport = dev->myhsport->portno;
+ } else {
+ hshubaddr = 0;
+ hshubport = 0;
+ }
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ if (addr == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &ehci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | EHCI_INTR_ENDPT:
+ pipe->methods = &ehci_root_intr_methods;
+ break;
+ default:
+ DPRINTF(("ehci_open: bad bEndpointAddress 0x%02x\n",
+ ed->bEndpointAddress));
+ return (USBD_INVAL);
+ }
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /* XXX All this stuff is only valid for async. */
+ switch (dev->speed) {
+ case USB_SPEED_LOW: speed = EHCI_QH_SPEED_LOW; break;
+ case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break;
+ case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break;
+ default: panic("ehci_open: bad device speed %d", dev->speed);
+ }
+ if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) {
+ printf("%s: *** Error: opening low/full speed isoc device on"
+ "ehci, this does not work yet. Feel free to implement\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
+ hshubaddr, hshubport));
+ return USBD_INVAL;
+ }
+
+ naks = 8; /* XXX */
+ /* Allocate sqh for everything, save isoc xfers */
+ if (xfertype != UE_ISOCHRONOUS) {
+ sqh = ehci_alloc_sqh(sc);
+ if (sqh == NULL)
+ goto bad0;
+ /* qh_link filled when the QH is added */
+ sqh->qh.qh_endp = htohc32(sc,
+ EHCI_QH_SET_ADDR(addr) |
+ EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
+ EHCI_QH_SET_EPS(speed) |
+ (xfertype == UE_CONTROL ? EHCI_QH_DTC : 0) |
+ EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
+ (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
+ EHCI_QH_CTL : 0) |
+ EHCI_QH_SET_NRL(naks)
+ );
+ sqh->qh.qh_endphub = htohc32(sc,
+ EHCI_QH_SET_MULT(1) |
+ EHCI_QH_SET_HUBA(hshubaddr) |
+ EHCI_QH_SET_PORT(hshubport) |
+ EHCI_QH_SET_CMASK(0x1c) |
+ EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
+ );
+ sqh->qh.qh_curqtd = EHCI_NULL(sc);
+ /* The overlay qTD was already set up by ehci_alloc_sqh(). */
+ sqh->qh.qh_qtd.qtd_status =
+ htohc32(sc, EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
+ epipe->sqh = sqh;
+ } else {
+ sqh = NULL;
+ }
+
+ switch (xfertype) {
+ case UE_CONTROL:
+ err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t),
+ 0, &epipe->u.ctl.reqdma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_open: usb_allocmem()=%d\n", err);
+#endif
+ if (err)
+ goto bad1;
+ pipe->methods = &ehci_device_ctrl_methods;
+ s = splusb();
+ ehci_add_qh(sc, sqh, sc->sc_async_head);
+ splx(s);
+ break;
+ case UE_BULK:
+ pipe->methods = &ehci_device_bulk_methods;
+ s = splusb();
+ ehci_add_qh(sc, sqh, sc->sc_async_head);
+ splx(s);
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &ehci_device_intr_methods;
+ ival = pipe->interval;
+ if (ival == USBD_DEFAULT_INTERVAL)
+ ival = ed->bInterval;
+ return (ehci_device_setintr(sc, sqh, ival));
+ case UE_ISOCHRONOUS:
+ pipe->methods = &ehci_device_isoc_methods;
+ if (ed->bInterval == 0 || ed->bInterval > 16) {
+ printf("ehci: opening pipe with invalid bInterval\n");
+ err = USBD_INVAL;
+ goto bad1;
+ }
+ if (UGETW(ed->wMaxPacketSize) == 0) {
+ printf("ehci: zero length endpoint open request\n");
+ err = USBD_INVAL;
+ goto bad1;
+ }
+ epipe->u.isoc.next_frame = 0;
+ epipe->u.isoc.cur_xfers = 0;
+ break;
+ default:
+ DPRINTF(("ehci: bad xfer type %d\n", xfertype));
+ return (USBD_INVAL);
+ }
+ return (USBD_NORMAL_COMPLETION);
+
+ bad1:
+ if (sqh != NULL)
+ ehci_free_sqh(sc, sqh);
+ return (err);
+ bad0:
+ return (USBD_NOMEM);
+}
+
+/*
+ * Add an ED to the schedule. Called at splusb().
+ * If in the async schedule, it will always have a next.
+ * If in the intr schedule it may not.
+ */
+void
+ehci_add_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
+{
+ SPLUSBCHECK;
+
+ sqh->next = head->next;
+ sqh->prev = head;
+ sqh->qh.qh_link = head->qh.qh_link;
+ head->next = sqh;
+ if (sqh->next)
+ sqh->next->prev = sqh;
+ head->qh.qh_link = htohc32(sc, sqh->physaddr | EHCI_LINK_QH);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 5) {
+ printf("ehci_add_qh:\n");
+ ehci_dump_sqh(sc, sqh);
+ }
+#endif
+}
+
+/*
+ * Remove an ED from the schedule. Called at splusb().
+ * Will always have a 'next' if it's in the async list as it's circular.
+ */
+void
+ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
+{
+ SPLUSBCHECK;
+ /* XXX */
+ sqh->prev->qh.qh_link = sqh->qh.qh_link;
+ sqh->prev->next = sqh->next;
+ if (sqh->next)
+ sqh->next->prev = sqh->prev;
+ ehci_sync_hc(sc);
+}
+
+/* Restart a QH following the addition of a qTD. */
+void
+ehci_activate_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd)
+{
+ KASSERT((sqtd->qtd.qtd_status & htohc32(sc, EHCI_QTD_ACTIVE)) == 0,
+ ("ehci_activate_qh: already active"));
+
+ /*
+ * When a QH is idle, the overlay qTD should be marked as not
+ * halted and not active. This causes the host controller to
+ * retrieve the real qTD on each pass (rather than just examinig
+ * the overlay), so it will notice when we activate the qTD.
+ */
+ if (sqtd == sqh->sqtd) {
+ /* Check that the hardware is in the state we expect. */
+ if (EHCI_LINK_ADDR(hc32toh(sc, sqh->qh.qh_qtd.qtd_next)) !=
+ sqtd->physaddr) {
+#ifdef EHCI_DEBUG
+ printf("ehci_activate_qh: unexpected next ptr\n");
+ ehci_dump_sqh(sc, sqh);
+ ehci_dump_sqtds(sc, sqh->sqtd);
+#endif
+ sqh->qh.qh_qtd.qtd_next = htohc32(sc, sqtd->physaddr);
+ sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL(sc);
+ }
+ /* Ensure the flags are correct. */
+ sqh->qh.qh_qtd.qtd_status &= htohc32(sc, EHCI_QTD_PINGSTATE |
+ EHCI_QTD_TOGGLE_MASK);
+ }
+
+ /* Now activate the qTD. */
+ sqtd->qtd.qtd_status |= htohc32(sc, EHCI_QTD_ACTIVE);
+}
+
+/*
+ * Ensure that the HC has released all references to the QH. We do this
+ * by asking for a Async Advance Doorbell interrupt and then we wait for
+ * the interrupt.
+ * To make this easier we first obtain exclusive use of the doorbell.
+ */
+void
+ehci_sync_hc(ehci_softc_t *sc)
+{
+ int s, error;
+
+ if (sc->sc_dying) {
+ DPRINTFN(2,("ehci_sync_hc: dying\n"));
+ return;
+ }
+ DPRINTFN(2,("ehci_sync_hc: enter\n"));
+ /* get doorbell */
+ lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL);
+ s = splhardusb();
+ /* ask for doorbell */
+ EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD);
+ DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n",
+ EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS)));
+ error = tsleep(&sc->sc_async_head, PZERO, "ehcidi", hz); /* bell wait */
+ DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n",
+ EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS)));
+ splx(s);
+ /* release doorbell */
+ lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL);
+#ifdef DIAGNOSTIC
+ if (error)
+ printf("ehci_sync_hc: tsleep() = %d\n", error);
+#endif
+ DPRINTFN(2,("ehci_sync_hc: exit\n"));
+}
+
+/*Call at splusb*/
+void
+ehci_rem_free_itd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer)
+{
+ struct ehci_soft_itd *itd, *prev;
+
+ prev = NULL;
+
+ if (exfer->itdstart == NULL || exfer->itdend == NULL)
+ panic("ehci isoc xfer being freed, but with no itd chain\n");
+
+ for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+ prev = itd->u.frame_list.prev;
+ /* Unlink itd from hardware chain, or frame array */
+ if (prev == NULL) { /* We're at the table head */
+ sc->sc_softitds[itd->slot] = itd->u.frame_list.next;
+ sc->sc_flist[itd->slot] = itd->itd.itd_next;
+
+ if (itd->u.frame_list.next != NULL)
+ itd->u.frame_list.next->u.frame_list.prev =
+ NULL;
+ } else {
+ /* XXX this part is untested... */
+ prev->itd.itd_next = itd->itd.itd_next;
+ prev->u.frame_list.next = itd->u.frame_list.next;
+ if (itd->u.frame_list.next != NULL)
+ itd->u.frame_list.next->u.frame_list.prev =
+ prev;
+ }
+ }
+
+ prev = NULL;
+ for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+ if (prev != NULL)
+ ehci_free_itd(sc, prev);
+ prev = itd;
+ }
+ if (prev)
+ ehci_free_itd(sc, prev);
+ exfer->itdstart = NULL;
+ exfer->itdend = NULL;
+}
+
+/***********/
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+static usb_device_descriptor_t ehci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_HSHUBSTT, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+static usb_device_qualifier_t ehci_odevd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE_QUALIFIER, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ 1, /* # of configurations */
+ 0
+};
+
+static usb_config_descriptor_t ehci_confd = {
+ USB_CONFIG_DESCRIPTOR_SIZE,
+ UDESC_CONFIG,
+ {USB_CONFIG_DESCRIPTOR_SIZE +
+ USB_INTERFACE_DESCRIPTOR_SIZE +
+ USB_ENDPOINT_DESCRIPTOR_SIZE},
+ 1,
+ 1,
+ 0,
+ UC_SELF_POWERED,
+ 0 /* max power */
+};
+
+static usb_interface_descriptor_t ehci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_HSHUBSTT,
+ 0
+};
+
+static usb_endpoint_descriptor_t ehci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | EHCI_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8, 0}, /* max packet */
+ 255
+};
+
+static usb_hub_descriptor_t ehci_hubd = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 0,
+ {0,0},
+ 0,
+ 0,
+ {0},
+};
+
+static int
+ehci_str(usb_string_descriptor_t *p, int l, char *s)
+{
+ int i;
+
+ if (l == 0)
+ return (0);
+ p->bLength = 2 * strlen(s) + 2;
+ if (l == 1)
+ return (1);
+ p->bDescriptorType = UDESC_STRING;
+ l -= 2;
+ for (i = 0; s[i] && l > 1; i++, l -= 2)
+ USETW2(p->bString[i], 0, s[i]);
+ return (2*i+2);
+}
+
+/*
+ * Simulate a hardware hub by handling all the necessary requests.
+ */
+static usbd_status
+ehci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_root_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ehci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ int port, i;
+ int s, len, value, index, l, totlen = 0;
+ usb_port_status_t ps;
+ usb_hub_descriptor_t hubd;
+ usbd_status err;
+ u_int32_t v;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ /* XXX panic */
+ return (USBD_INVAL);
+#endif
+ req = &xfer->request;
+
+ DPRINTFN(4,("ehci_root_ctrl_start: type=0x%02x request=%02x\n",
+ req->bmRequestType, req->bRequest));
+
+ len = UGETW(req->wLength);
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ if (len != 0)
+ buf = xfer->buffer;
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch(C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ if (len > 0) {
+ *(u_int8_t *)buf = sc->sc_conf;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ DPRINTFN(8,("ehci_root_ctrl_start: wValue=0x%04x\n", value));
+ switch(value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ USETW(ehci_devd.idVendor, sc->sc_id_vendor);
+ memcpy(buf, &ehci_devd, l);
+ break;
+ /*
+ * We can't really operate at another speed, but the spec says
+ * we need this descriptor.
+ */
+ case UDESC_DEVICE_QUALIFIER:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &ehci_odevd, l);
+ break;
+ /*
+ * We can't really operate at another speed, but the spec says
+ * we need this descriptor.
+ */
+ case UDESC_OTHER_SPEED_CONFIGURATION:
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &ehci_confd, l);
+ ((usb_config_descriptor_t *)buf)->bDescriptorType =
+ value >> 8;
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ehci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ehci_endpd, l);
+ break;
+ case UDESC_STRING:
+ if (len == 0)
+ break;
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ totlen = ehci_str(buf, len, "\001");
+ break;
+ case 1: /* Vendor */
+ totlen = ehci_str(buf, len, sc->sc_vendor);
+ break;
+ case 2: /* Product */
+ totlen = ehci_str(buf, len, "EHCI root hub");
+ break;
+ }
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ if (len > 0) {
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED);
+ totlen = 2;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, 0);
+ totlen = 2;
+ }
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(8, ("ehci_root_ctrl_start: UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR;
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ EOWRITE4(sc, port, v &~ EHCI_PS_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ EOWRITE4(sc, port, v &~ EHCI_PS_SUSP);
+ break;
+ case UHF_PORT_POWER:
+ EOWRITE4(sc, port, v &~ EHCI_PS_PP);
+ break;
+ case UHF_PORT_TEST:
+ DPRINTFN(2,("ehci_root_ctrl_start: clear port test "
+ "%d\n", index));
+ break;
+ case UHF_PORT_INDICATOR:
+ DPRINTFN(2,("ehci_root_ctrl_start: clear port ind "
+ "%d\n", index));
+ EOWRITE4(sc, port, v &~ EHCI_PS_PIC);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ EOWRITE4(sc, port, v | EHCI_PS_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ EOWRITE4(sc, port, v | EHCI_PS_PEC);
+ break;
+ case UHF_C_PORT_SUSPEND:
+ /* how? */
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ EOWRITE4(sc, port, v | EHCI_PS_OCC);
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ hubd = ehci_hubd;
+ hubd.bNbrPorts = sc->sc_noport;
+ v = EOREAD4(sc, EHCI_HCSPARAMS);
+ USETW(hubd.wHubCharacteristics,
+ EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH |
+ EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS))
+ ? UHD_PORT_IND : 0);
+ hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */
+ for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8)
+ hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */
+ hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
+ l = min(len, hubd.bDescLength);
+ totlen = l;
+ memcpy(buf, &hubd, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len); /* ? XXX */
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(8,("ehci_root_ctrl_start: get port status i=%d\n",
+ index));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = EOREAD4(sc, EHCI_PORTSC(index));
+ DPRINTFN(8,("ehci_root_ctrl_start: port status=0x%04x\n",
+ v));
+
+ i = UPS_HIGH_SPEED;
+
+ if (sc->sc_flags & (EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_TT)) {
+ if ((v & 0xc000000) == 0x8000000)
+ i = UPS_HIGH_SPEED;
+ else if ((v & 0xc000000) == 0x4000000)
+ i = UPS_LOW_SPEED;
+ else
+ i = 0;
+ }
+
+ if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS;
+ if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED;
+ if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND;
+ if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR;
+ if (v & EHCI_PS_PR) i |= UPS_RESET;
+ if (v & EHCI_PS_PP) i |= UPS_PORT_POWER;
+ USETW(ps.wPortStatus, i);
+ i = 0;
+ if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS;
+ if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED;
+ if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR;
+ if (sc->sc_isreset) i |= UPS_C_PORT_RESET;
+ USETW(ps.wPortChange, i);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR;
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ EOWRITE4(sc, port, v | EHCI_PS_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ EOWRITE4(sc, port, v | EHCI_PS_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ DPRINTFN(5,("ehci_root_ctrl_start: reset port %d\n",
+ index));
+ if (EHCI_PS_IS_LOWSPEED(v) &&
+ (sc->sc_flags & EHCI_SCFLG_TT) == 0) {
+ /* Low speed device, give up ownership. */
+ ehci_disown(sc, index, 1);
+ break;
+ }
+ /* Start reset sequence. */
+ v &= ~ (EHCI_PS_PE | EHCI_PS_PR);
+ EOWRITE4(sc, port, v | EHCI_PS_PR);
+ /* Wait for reset to complete. */
+ usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ /* Terminate reset sequence. */
+ if (sc->sc_flags & EHCI_SCFLG_NORESTERM)
+ ;
+ else
+ EOWRITE4(sc, port, v);
+
+ /* Wait for HC to complete reset. */
+ usb_delay_ms(&sc->sc_bus, EHCI_PORT_RESET_COMPLETE);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = EOREAD4(sc, port);
+ DPRINTF(("ehci after reset, status=0x%08x\n", v));
+ if (v & EHCI_PS_PR) {
+ printf("%s: port reset timeout\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ return (USBD_TIMEOUT);
+ }
+ if (!(v & EHCI_PS_PE) &&
+ (sc->sc_flags & EHCI_SCFLG_TT) == 0) {
+ /* Not a high speed device, give up ownership.*/
+ ehci_disown(sc, index, 0);
+ break;
+ }
+ sc->sc_isreset = 1;
+ DPRINTF(("ehci port %d reset, status = 0x%08x\n",
+ index, v));
+ break;
+ case UHF_PORT_POWER:
+ DPRINTFN(2,("ehci_root_ctrl_start: set port power "
+ "%d\n", index));
+ EOWRITE4(sc, port, v | EHCI_PS_PP);
+ break;
+ case UHF_PORT_TEST:
+ DPRINTFN(2,("ehci_root_ctrl_start: set port test "
+ "%d\n", index));
+ break;
+ case UHF_PORT_INDICATOR:
+ DPRINTFN(2,("ehci_root_ctrl_start: set port ind "
+ "%d\n", index));
+ EOWRITE4(sc, port, v | EHCI_PS_PIC);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
+ case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
+ case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
+ case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ ret:
+ xfer->status = err;
+ s = splusb();
+ hacksync(xfer); /* XXX to compensate for usb_transfer_complete */
+ usb_transfer_complete(xfer);
+ splx(s);
+ return (USBD_IN_PROGRESS);
+}
+
+void
+ehci_disown(ehci_softc_t *sc, int index, int lowspeed)
+{
+ int port;
+ u_int32_t v;
+
+ DPRINTF(("ehci_disown: index=%d lowspeed=%d\n", index, lowspeed));
+#ifdef DIAGNOSTIC
+ if (sc->sc_npcomp != 0) {
+ int i = (index-1) / sc->sc_npcomp;
+ if (i >= sc->sc_ncomp)
+ printf("%s: strange port\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ else
+ printf("%s: handing over %s speed device on "
+ "port %d to %s\n",
+ device_get_nameunit(sc->sc_bus.bdev),
+ lowspeed ? "low" : "full",
+ index, device_get_nameunit(sc->sc_comps[i]->bdev));
+ } else {
+ printf("%s: npcomp == 0\n", device_get_nameunit(sc->sc_bus.bdev));
+ }
+#endif
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR;
+ EOWRITE4(sc, port, v | EHCI_PS_PO);
+}
+
+/* Abort a root control request. */
+static void
+ehci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+/* Close the root pipe. */
+static void
+ehci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("ehci_root_ctrl_close\n"));
+ /* Nothing to do. */
+}
+
+void
+ehci_root_intr_done(usbd_xfer_handle xfer)
+{
+}
+
+static usbd_status
+ehci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_root_intr_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ehci_root_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ sc->sc_intrxfer = xfer;
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root interrupt request. */
+static void
+ehci_root_intr_abort(usbd_xfer_handle xfer)
+{
+ int s;
+
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("ehci_root_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ xfer->status = USBD_CANCELLED;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+}
+
+/* Close the root pipe. */
+static void
+ehci_root_intr_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ehci_root_intr_close\n"));
+
+ sc->sc_intrxfer = NULL;
+}
+
+void
+ehci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+}
+
+/************************/
+
+ehci_soft_qh_t *
+ehci_alloc_sqh(ehci_softc_t *sc)
+{
+ ehci_soft_qh_t *sqh;
+ ehci_soft_qtd_t *sqtd;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeqhs == NULL) {
+ DPRINTFN(2, ("ehci_alloc_sqh: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK,
+ EHCI_PAGE_SIZE, &dma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err);
+#endif
+ if (err)
+ return (NULL);
+ for(i = 0; i < EHCI_SQH_CHUNK; i++) {
+ offs = i * EHCI_SQH_SIZE;
+ sqh = KERNADDR(&dma, offs);
+ sqh->physaddr = DMAADDR(&dma, offs);
+ sqh->next = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+ }
+ }
+ /* Allocate the initial inactive sqtd. */
+ sqtd = ehci_alloc_sqtd(sc);
+ if (sqtd == NULL)
+ return (NULL);
+ sqtd->qtd.qtd_status = htohc32(sc, 0);
+ sqtd->qtd.qtd_next = EHCI_NULL(sc);
+ sqtd->qtd.qtd_altnext = EHCI_NULL(sc);
+
+ sqh = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh->next;
+
+ /* The overlay QTD should begin zeroed. */
+ sqh->qh.qh_qtd.qtd_next = htohc32(sc, sqtd->physaddr);
+ sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL(sc);
+ sqh->qh.qh_qtd.qtd_status = 0;
+ for (i = 0; i < EHCI_QTD_NBUFFERS; i++) {
+ sqh->qh.qh_qtd.qtd_buffer[i] = 0;
+ sqh->qh.qh_qtd.qtd_buffer_hi[i] = 0;
+ }
+ sqh->next = NULL;
+ sqh->prev = NULL;
+ sqh->sqtd = sqtd;
+ sqh->inactivesqtd = sqtd;
+ return (sqh);
+}
+
+void
+ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh)
+{
+ ehci_free_sqtd(sc, sqh->inactivesqtd);
+ sqh->next = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+}
+
+ehci_soft_qtd_t *
+ehci_alloc_sqtd(ehci_softc_t *sc)
+{
+ ehci_soft_qtd_t *sqtd;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+ int s;
+
+ if (sc->sc_freeqtds == NULL) {
+ DPRINTFN(2, ("ehci_alloc_sqtd: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK,
+ EHCI_PAGE_SIZE, &dma);
+#ifdef EHCI_DEBUG
+ if (err)
+ printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err);
+#endif
+ if (err)
+ return (NULL);
+ s = splusb();
+ for(i = 0; i < EHCI_SQTD_CHUNK; i++) {
+ offs = i * EHCI_SQTD_SIZE;
+ sqtd = KERNADDR(&dma, offs);
+ sqtd->physaddr = DMAADDR(&dma, offs);
+ sqtd->nextqtd = sc->sc_freeqtds;
+ sc->sc_freeqtds = sqtd;
+ }
+ splx(s);
+ }
+
+ s = splusb();
+ sqtd = sc->sc_freeqtds;
+ sc->sc_freeqtds = sqtd->nextqtd;
+ sqtd->qtd.qtd_next = EHCI_NULL(sc);
+ sqtd->qtd.qtd_altnext = EHCI_NULL(sc);
+ sqtd->qtd.qtd_status = 0;
+ for (i = 0; i < EHCI_QTD_NBUFFERS; i++) {
+ sqtd->qtd.qtd_buffer[i] = 0;
+ sqtd->qtd.qtd_buffer_hi[i] = 0;
+ }
+ sqtd->nextqtd = NULL;
+ sqtd->xfer = NULL;
+ splx(s);
+
+ return (sqtd);
+}
+
+void
+ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
+{
+ int s;
+
+ s = splusb();
+ sqtd->nextqtd = sc->sc_freeqtds;
+ sc->sc_freeqtds = sqtd;
+ splx(s);
+}
+
+usbd_status
+ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
+ int alen, int rd, usbd_xfer_handle xfer, ehci_soft_qtd_t *start,
+ ehci_soft_qtd_t *newinactive, ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep)
+{
+ ehci_soft_qtd_t *next, *cur;
+ ehci_physaddr_t dataphys, nextphys;
+ u_int32_t qtdstatus;
+ int adj, len, curlen, mps, offset, pagelen, seg, segoff;
+ int i, iscontrol, forceshort;
+ struct usb_dma_mapping *dma = &xfer->dmamap;
+
+ DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen));
+
+ offset = 0;
+ len = alen;
+ iscontrol = (epipe->pipe.endpoint->edesc->bmAttributes & UE_XFERTYPE) ==
+ UE_CONTROL;
+ qtdstatus = EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) |
+ EHCI_QTD_SET_CERR(3)
+ /* IOC set below */
+ /* BYTES set below */
+ ;
+ mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
+ forceshort = ((xfer->flags & USBD_FORCE_SHORT_XFER) || len == 0) &&
+ len % mps == 0;
+ /*
+ * The control transfer data stage always starts with a toggle of 1.
+ * For other transfers we let the hardware track the toggle state.
+ */
+ if (iscontrol)
+ qtdstatus |= EHCI_QTD_SET_TOGGLE(1);
+
+ if (start != NULL) {
+ /*
+ * If we are given a starting qTD, assume it is linked into
+ * an active QH so be careful not to mark it active.
+ */
+ cur = start;
+ *sp = cur;
+ qtdstatus &= ~EHCI_QTD_ACTIVE;
+ } else {
+ cur = ehci_alloc_sqtd(sc);
+ *sp = cur;
+ if (cur == NULL)
+ goto nomem;
+ }
+ seg = 0;
+ segoff = 0;
+ for (;;) {
+ curlen = 0;
+
+ /* The EHCI hardware can handle at most 5 pages. */
+ for (i = 0; i < EHCI_QTD_NBUFFERS && curlen < len; i++) {
+ KASSERT(seg < dma->nsegs,
+ ("ehci_alloc_sqtd_chain: overrun"));
+ dataphys = dma->segs[seg].ds_addr + segoff;
+ pagelen = dma->segs[seg].ds_len - segoff;
+ if (pagelen > len - curlen)
+ pagelen = len - curlen;
+ if (pagelen > EHCI_PAGE_SIZE -
+ EHCI_PAGE_OFFSET(dataphys))
+ pagelen = EHCI_PAGE_SIZE -
+ EHCI_PAGE_OFFSET(dataphys);
+ segoff += pagelen;
+ if (segoff >= dma->segs[seg].ds_len) {
+ KASSERT(segoff == dma->segs[seg].ds_len,
+ ("ehci_alloc_sqtd_chain: overlap"));
+ seg++;
+ segoff = 0;
+ }
+
+ cur->qtd.qtd_buffer[i] = htohc32(sc, dataphys);
+ cur->qtd.qtd_buffer_hi[i] = 0;
+ curlen += pagelen;
+
+ /*
+ * Must stop if there is any gap before or after
+ * the page boundary.
+ */
+ if (EHCI_PAGE_OFFSET(dataphys + pagelen) != 0)
+ break;
+ if (seg < dma->nsegs && EHCI_PAGE_OFFSET(segoff +
+ dma->segs[seg].ds_addr) != 0)
+ break;
+ }
+ /* Adjust down to a multiple of mps if not at the end. */
+ if (curlen < len && curlen % mps != 0) {
+ adj = curlen % mps;
+ curlen -= adj;
+ KASSERT(curlen > 0,
+ ("ehci_alloc_sqtd_chain: need to copy"));
+ segoff -= adj;
+ if (segoff < 0) {
+ seg--;
+ segoff += dma->segs[seg].ds_len;
+ }
+ KASSERT(seg >= 0 && segoff >= 0,
+ ("ehci_alloc_sqtd_chain: adjust to mps"));
+ }
+
+ len -= curlen;
+
+ if (len != 0 || forceshort) {
+ next = ehci_alloc_sqtd(sc);
+ if (next == NULL)
+ goto nomem;
+ nextphys = htohc32(sc, next->physaddr);
+ } else {
+ next = NULL;
+ nextphys = EHCI_NULL(sc);
+ }
+
+ cur->nextqtd = next;
+ cur->qtd.qtd_next = nextphys;
+ /* Make sure to stop after a short transfer. */
+ cur->qtd.qtd_altnext = htohc32(sc, newinactive->physaddr);
+ cur->qtd.qtd_status =
+ htohc32(sc, qtdstatus | EHCI_QTD_SET_BYTES(curlen));
+ cur->xfer = xfer;
+ cur->len = curlen;
+ DPRINTFN(10,("ehci_alloc_sqtd_chain: curlen=%d\n", curlen));
+ if (iscontrol) {
+ /*
+ * adjust the toggle based on the number of packets
+ * in this qtd
+ */
+ if ((((curlen + mps - 1) / mps) & 1) || curlen == 0)
+ qtdstatus ^= EHCI_QTD_TOGGLE_MASK;
+ }
+ qtdstatus |= EHCI_QTD_ACTIVE;
+ if (len == 0) {
+ if (!forceshort)
+ break;
+ forceshort = 0;
+ }
+ DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n"));
+ offset += curlen;
+ cur = next;
+ }
+ cur->qtd.qtd_status |= htohc32(sc, EHCI_QTD_IOC);
+ *ep = cur;
+
+ DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
+ *sp, *ep));
+
+ return (USBD_NORMAL_COMPLETION);
+
+ nomem:
+ /* XXX free chain */
+ DPRINTFN(-1,("ehci_alloc_sqtd_chain: no memory\n"));
+ return (USBD_NOMEM);
+}
+
+/* Free the chain starting at sqtd and end at the qTD before sqtdend */
+static void
+ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qh_t *sqh,
+ ehci_soft_qtd_t *sqtd, ehci_soft_qtd_t *sqtdend)
+{
+ ehci_soft_qtd_t *p, **prevp;
+ int i;
+
+ DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p sqtdend=%p\n",
+ sqtd, sqtdend));
+
+ /* First unlink the chain from the QH's software qTD list. */
+ prevp = &sqh->sqtd;
+ for (p = sqh->sqtd; p != NULL; p = p->nextqtd) {
+ if (p == sqtd) {
+ *prevp = sqtdend;
+ break;
+ }
+ prevp = &p->nextqtd;
+ }
+ KASSERT(p != NULL, ("ehci_free_sqtd_chain: chain not found"));
+ for (i = 0; sqtd != sqtdend; sqtd = p, i++) {
+ p = sqtd->nextqtd;
+ ehci_free_sqtd(sc, sqtd);
+ }
+}
+
+ehci_soft_itd_t *
+ehci_alloc_itd(ehci_softc_t *sc)
+{
+ struct ehci_soft_itd *itd, *freeitd;
+ usbd_status err;
+ int i, s, offs, frindex, previndex;
+ usb_dma_t dma;
+
+ s = splusb();
+
+ /* Find an itd that wasn't freed this frame or last frame. This can
+ * discard itds that were freed before frindex wrapped around
+ * XXX - can this lead to thrashing? Could fix by enabling wrap-around
+ * interrupt and fiddling with list when that happens */
+ frindex = (EOREAD4(sc, EHCI_FRINDEX) + 1) >> 3;
+ previndex = (frindex != 0) ? frindex - 1 : sc->sc_flsize;
+
+ freeitd = NULL;
+ LIST_FOREACH(itd, &sc->sc_freeitds, u.free_list) {
+ if (itd == NULL)
+ break;
+ if (itd->slot != frindex && itd->slot != previndex) {
+ freeitd = itd;
+ break;
+ }
+ }
+
+ if (freeitd == NULL) {
+ DPRINTFN(2, ("ehci_alloc_itd allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK,
+ EHCI_PAGE_SIZE, &dma);
+
+ if (err) {
+ DPRINTF(("ehci_alloc_itd, alloc returned %d\n", err));
+ return NULL;
+ }
+
+ for (i = 0; i < EHCI_ITD_CHUNK; i++) {
+ offs = i * EHCI_ITD_SIZE;
+ itd = KERNADDR(&dma, offs);
+ itd->physaddr = DMAADDR(&dma, offs);
+ itd->dma = dma;
+ itd->offs = offs;
+ LIST_INSERT_HEAD(&sc->sc_freeitds, itd, u.free_list);
+ }
+ freeitd = LIST_FIRST(&sc->sc_freeitds);
+ }
+
+ itd = freeitd;
+ LIST_REMOVE(itd, u.free_list);
+ memset(&itd->itd, 0, sizeof(ehci_itd_t));
+ itd->u.frame_list.next = NULL;
+ itd->u.frame_list.prev = NULL;
+ itd->xfer_next = NULL;
+ itd->slot = 0;
+ splx(s);
+
+ return (itd);
+}
+
+void
+ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd)
+{
+ int s;
+
+ s = splusb();
+ LIST_INSERT_AFTER(LIST_FIRST(&sc->sc_freeitds), itd, u.free_list);
+ splx(s);
+}
+
+/****************/
+
+/*
+ * Close a reqular pipe.
+ * Assumes that there are no pending transactions.
+ */
+void
+ehci_close_pipe(usbd_pipe_handle pipe, ehci_soft_qh_t *head)
+{
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+ ehci_soft_qh_t *sqh = epipe->sqh;
+ int s;
+
+ s = splusb();
+ ehci_rem_qh(sc, sqh, head);
+ splx(s);
+ pipe->endpoint->savedtoggle =
+ EHCI_QTD_GET_TOGGLE(hc32toh(sc, sqh->qh.qh_qtd.qtd_status));
+ ehci_free_sqh(sc, epipe->sqh);
+}
+
+/*
+ * Abort a device request.
+ * If this routine is called at splusb() it guarantees that the request
+ * will be removed from the hardware scheduling and that the callback
+ * for it will be called with USBD_CANCELLED status.
+ * It's impossible to guarantee that the requested transfer will not
+ * have happened since the hardware runs concurrently.
+ * If the transaction has already happened we rely on the ordinary
+ * interrupt processing to process it.
+ */
+void
+ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+#define exfer EXFER(xfer)
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
+ ehci_soft_qh_t *sqh = epipe->sqh;
+ ehci_soft_qtd_t *sqtd, *snext;
+ ehci_physaddr_t cur, us, next;
+ int s;
+ int hit, i;
+ /* int count = 0; */
+ ehci_soft_qh_t *psqh;
+
+ DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));
+
+ if (sc->sc_dying) {
+ /* If we're dying, just do the software part. */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(epipe->pipe.device, &exfer->abort_task);
+ usb_transfer_complete(xfer);
+ splx(s);
+ return;
+ }
+
+ if (xfer->device->bus->intr_context)
+ panic("ehci_abort_xfer: not in process context");
+
+ /*
+ * If an abort is already in progress then just wait for it to
+ * complete and return.
+ */
+ if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING) {
+ DPRINTFN(2, ("ehci_abort_xfer: already aborting\n"));
+ /* No need to wait if we're aborting from a timeout. */
+ if (status == USBD_TIMEOUT)
+ return;
+ /* Override the status which might be USBD_TIMEOUT. */
+ xfer->status = status;
+ DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
+ exfer->ehci_xfer_flags |= EHCI_XFER_ABORTWAIT;
+ while (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING)
+ tsleep(&exfer->ehci_xfer_flags, PZERO, "ehciaw", 0);
+ return;
+ }
+
+ /*
+ * Step 1: Make interrupt routine and timeouts ignore xfer.
+ */
+ s = splusb();
+ exfer->ehci_xfer_flags |= EHCI_XFER_ABORTING;
+ xfer->status = status; /* make software ignore it */
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(epipe->pipe.device, &exfer->abort_task);
+ splx(s);
+
+ /*
+ * Step 2: Wait until we know hardware has finished any possible
+ * use of the xfer. We do this by removing the entire
+ * queue from the async schedule and waiting for the doorbell.
+ * Nothing else should be touching the queue now.
+ */
+ psqh = sqh->prev;
+ ehci_rem_qh(sc, sqh, psqh);
+
+ /*
+ * Step 3: make sure the soft interrupt routine
+ * has run. This should remove any completed items off the queue.
+ * The hardware has no reference to completed items (TDs).
+ * It's safe to remove them at any time.
+ */
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
+#endif /* USB_USE_SOFTINTR */
+
+ /*
+ * Step 4: Remove any vestiges of the xfer from the hardware.
+ * The complication here is that the hardware may have executed
+ * into or even beyond the xfer we're trying to abort.
+ * So as we're scanning the TDs of this xfer we check if
+ * the hardware points to any of them.
+ *
+ * first we need to see if there are any transfers
+ * on this queue before the xfer we are aborting.. we need
+ * to update any pointers that point to us to point past
+ * the aborting xfer. (If there is something past us).
+ * Hardware and software.
+ */
+ cur = EHCI_LINK_ADDR(hc32toh(sc, sqh->qh.qh_curqtd));
+ hit = 0;
+
+ /* If they initially point here. */
+ us = exfer->sqtdstart->physaddr;
+
+ /* We will change them to point here */
+ snext = exfer->sqtdend->nextqtd;
+ next = (snext != NULL) ? htohc32(sc, snext->physaddr) : EHCI_NULL(sc);
+
+ /*
+ * Now loop through any qTDs before us and keep track of the pointer
+ * that points to us for the end.
+ */
+ sqtd = sqh->sqtd;
+ while (sqtd && sqtd != exfer->sqtdstart) {
+ hit |= (cur == sqtd->physaddr);
+ if (EHCI_LINK_ADDR(hc32toh(sc, sqtd->qtd.qtd_next)) == us)
+ sqtd->qtd.qtd_next = next;
+ if (EHCI_LINK_ADDR(hc32toh(sc, sqtd->qtd.qtd_altnext)) == us)
+ sqtd->qtd.qtd_altnext = next;
+ sqtd = sqtd->nextqtd;
+ }
+
+ /*
+ * If we already saw the active one then we are pretty much done.
+ * We've done all the relinking we need to do.
+ */
+ if (!hit) {
+
+ /*
+ * Now reinitialise the QH to point to the next qTD
+ * (if there is one). We only need to do this if
+ * it was previously pointing to us.
+ */
+ for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) {
+ if (cur == sqtd->physaddr) {
+ hit++;
+ }
+ if (sqtd == exfer->sqtdend)
+ break;
+ }
+ sqtd = sqtd->nextqtd;
+ /*
+ * Only need to alter the QH if it was pointing at a qTD
+ * that we are removing.
+ */
+ if (hit) {
+ sqh->qh.qh_qtd.qtd_next = htohc32(sc, snext->physaddr);
+ sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL(sc);
+ sqh->qh.qh_qtd.qtd_status &=
+ htohc32(sc, EHCI_QTD_TOGGLE_MASK);
+ for (i = 0; i < EHCI_QTD_NBUFFERS; i++) {
+ sqh->qh.qh_qtd.qtd_buffer[i] = 0;
+ sqh->qh.qh_qtd.qtd_buffer_hi[i] = 0;
+ }
+ }
+ }
+ ehci_add_qh(sc, sqh, psqh);
+ /*
+ * Step 5: Execute callback.
+ */
+#ifdef DIAGNOSTIC
+ exfer->isdone = 1;
+#endif
+ /* Do the wakeup first to avoid touching the xfer after the callback. */
+ exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTING;
+ if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTWAIT) {
+ exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTWAIT;
+ wakeup(&exfer->ehci_xfer_flags);
+ }
+ usb_transfer_complete(xfer);
+
+ /* printf("%s: %d TDs aborted\n", __func__, count); */
+ splx(s);
+#undef exfer
+}
+
+void
+ehci_timeout(void *addr)
+{
+ struct ehci_xfer *exfer = addr;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)exfer->xfer.pipe;
+ ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
+
+ DPRINTF(("ehci_timeout: exfer=%p\n", exfer));
+#ifdef USB_DEBUG
+ if (ehcidebug > 1)
+ usbd_dump_pipe(exfer->xfer.pipe);
+#endif
+
+ if (sc->sc_dying) {
+ ehci_abort_xfer(&exfer->xfer, USBD_TIMEOUT);
+ return;
+ }
+
+ /* Execute the abort in a process context. */
+ usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task,
+ USB_TASKQ_HC);
+}
+
+void
+ehci_abort_isoc_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ ehci_isoc_trans_t trans_status;
+ struct ehci_pipe *epipe;
+ struct ehci_xfer *exfer;
+ ehci_softc_t *sc;
+ struct ehci_soft_itd *itd;
+ int s, i;
+
+ epipe = (struct ehci_pipe *) xfer->pipe;
+ exfer = EXFER(xfer);
+ sc = (ehci_softc_t *)epipe->pipe.device->bus;
+
+ DPRINTF(("ehci_abort_isoc_xfer: xfer %p pipe %p\n", xfer, epipe));
+
+ if (sc->sc_dying) {
+ s = splusb();
+ xfer->status = status;
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(epipe->pipe.device, &exfer->abort_task);
+ usb_transfer_complete(xfer);
+ splx(s);
+ return;
+ }
+
+ if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING) {
+ DPRINTFN(2, ("ehci_abort_isoc_xfer: already aborting\n"));
+
+#ifdef DIAGNOSTIC
+ if (status == USBD_TIMEOUT)
+ printf("ehci_abort_xfer: TIMEOUT while aborting\n");
+#endif
+
+ xfer->status = status;
+ DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
+ exfer->ehci_xfer_flags |= EHCI_XFER_ABORTWAIT;
+ while (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING)
+ tsleep(&exfer->ehci_xfer_flags, PZERO, "ehciaw", 0);
+ return;
+ }
+ exfer->ehci_xfer_flags |= EHCI_XFER_ABORTING;
+
+ xfer->status = status;
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(epipe->pipe.device, &exfer->abort_task);
+
+ s = splusb();
+ for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+
+ for (i = 0; i < 8; i++) {
+ trans_status = hc32toh(sc, itd->itd.itd_ctl[i]);
+ trans_status &= ~EHCI_ITD_ACTIVE;
+ itd->itd.itd_ctl[i] = htohc32(sc, trans_status);
+ }
+
+ }
+ splx(s);
+
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
+#endif /* USB_USE_SOFTINTR */
+ splx(s);
+
+#ifdef DIAGNOSTIC
+ exfer->isdone = 1;
+#endif
+ exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTING;
+ if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTWAIT) {
+ exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTWAIT;
+ wakeup(&exfer->ehci_xfer_flags);
+ }
+ usb_transfer_complete(xfer);
+}
+
+void
+ehci_timeout_task(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ int s;
+
+ DPRINTF(("ehci_timeout_task: xfer=%p\n", xfer));
+
+ s = splusb();
+ ehci_abort_xfer(xfer, USBD_TIMEOUT);
+ splx(s);
+}
+
+/*
+ * Some EHCI chips from VIA / ATI seem to trigger interrupts before writing
+ * back the qTD status, or miss signalling occasionally under heavy load.
+ * If the host machine is too fast, we can miss transaction completion - when
+ * we scan the active list the transaction still seems to be active. This
+ * generally exhibits itself as a umass stall that never recovers.
+ *
+ * We work around this behaviour by setting up this callback after any softintr
+ * that completes with transactions still pending, giving us another chance to
+ * check for completion after the writeback has taken place.
+ */
+void
+ehci_intrlist_timeout(void *arg)
+{
+ ehci_softc_t *sc = arg;
+ int s = splusb();
+
+ DPRINTFN(3, ("ehci_intrlist_timeout\n"));
+ usb_schedsoftintr(&sc->sc_bus);
+
+ splx(s);
+}
+
+/************************/
+
+static usbd_status
+ehci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_device_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ehci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ /* XXX panic */
+ printf("ehci_device_ctrl_transfer: not a request\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ err = ehci_device_request(xfer);
+ if (err)
+ return (err);
+
+ if (sc->sc_bus.use_polling)
+ ehci_waitintr(sc, xfer);
+ return (USBD_IN_PROGRESS);
+}
+
+void
+ehci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ struct ehci_xfer *ex = EXFER(xfer);
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+
+ DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer));
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ panic("ehci_ctrl_done: not a request");
+ }
+#endif
+
+ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
+ ehci_del_intr_list(ex); /* remove from active list */
+ ehci_free_sqtd_chain(sc, epipe->sqh, ex->sqtdstart,
+ ex->sqtdend->nextqtd);
+ }
+
+ DPRINTFN(5, ("ehci_ctrl_done: length=%d\n", xfer->actlen));
+}
+
+/* Abort a device control request. */
+static void
+ehci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ehci_device_ctrl_abort: xfer=%p\n", xfer));
+ ehci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+static void
+ehci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+ /*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/
+
+ DPRINTF(("ehci_device_ctrl_close: pipe=%p\n", pipe));
+ ehci_close_pipe(pipe, sc->sc_async_head);
+}
+
+usbd_status
+ehci_device_request(usbd_xfer_handle xfer)
+{
+#define exfer EXFER(xfer)
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ usb_device_request_t *req = &xfer->request;
+ usbd_device_handle dev = epipe->pipe.device;
+ ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
+ ehci_soft_qtd_t *newinactive, *setup, *stat, *next;
+ ehci_soft_qh_t *sqh;
+ int isread;
+ int len;
+ usbd_status err;
+ int s;
+
+ isread = req->bmRequestType & UT_READ;
+ len = UGETW(req->wLength);
+
+ DPRINTFN(3,("ehci_device_request: type=0x%02x, request=0x%02x, "
+ "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n",
+ req->bmRequestType, req->bRequest, UGETW(req->wValue),
+ UGETW(req->wIndex), len, dev->address,
+ epipe->pipe.endpoint->edesc->bEndpointAddress));
+
+ newinactive = ehci_alloc_sqtd(sc);
+ if (newinactive == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ newinactive->qtd.qtd_status = htohc32(sc, 0);
+ newinactive->qtd.qtd_next = EHCI_NULL(sc);
+ newinactive->qtd.qtd_altnext = EHCI_NULL(sc);
+
+ stat = ehci_alloc_sqtd(sc);
+ if (stat == NULL) {
+ err = USBD_NOMEM;
+ goto bad2;
+ }
+
+ sqh = epipe->sqh;
+ setup = sqh->inactivesqtd;
+ sqh->inactivesqtd = newinactive;
+ epipe->u.ctl.length = len;
+
+ /* Set up data transaction */
+ if (len != 0) {
+ ehci_soft_qtd_t *end;
+
+ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
+ NULL, newinactive, &next, &end);
+ if (err)
+ goto bad3;
+ end->qtd.qtd_status &= htohc32(sc, ~EHCI_QTD_IOC);
+ end->nextqtd = stat;
+ end->qtd.qtd_next = htohc32(sc, stat->physaddr);
+ end->qtd.qtd_altnext = htohc32(sc, newinactive->physaddr);
+ } else {
+ next = stat;
+ }
+
+ memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req);
+
+ /* Clear toggle, and do not activate until complete */
+ setup->qtd.qtd_status = htohc32(sc,
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
+ EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(0) |
+ EHCI_QTD_SET_BYTES(sizeof *req)
+ );
+ setup->qtd.qtd_buffer[0] = htohc32(sc, DMAADDR(&epipe->u.ctl.reqdma, 0));
+ setup->qtd.qtd_buffer_hi[0] = 0;
+ setup->nextqtd = next;
+ setup->qtd.qtd_next = htohc32(sc, next->physaddr);
+ setup->qtd.qtd_altnext = htohc32(sc, newinactive->physaddr);
+ setup->xfer = xfer;
+ setup->len = sizeof *req;
+
+ stat->qtd.qtd_status = htohc32(sc,
+ EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) |
+ EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(1) |
+ EHCI_QTD_IOC
+ );
+ stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */
+ stat->qtd.qtd_buffer_hi[0] = 0; /* XXX not needed? */
+ stat->nextqtd = newinactive;
+ stat->qtd.qtd_next = htohc32(sc, newinactive->physaddr);
+ stat->qtd.qtd_altnext = htohc32(sc, newinactive->physaddr);
+ stat->xfer = xfer;
+ stat->len = 0;
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 5) {
+ DPRINTF(("ehci_device_request:\n"));
+ ehci_dump_sqh(sc, sqh);
+ ehci_dump_sqtds(sc, setup);
+ }
+#endif
+
+ exfer->sqtdstart = setup;
+ exfer->sqtdend = stat;
+#ifdef DIAGNOSTIC
+ if (!exfer->isdone) {
+ printf("ehci_device_request: not done, exfer=%p\n", exfer);
+ }
+ exfer->isdone = 0;
+#endif
+
+ /* Activate the new qTD in the QH list. */
+ s = splusb();
+ ehci_activate_qh(sc, sqh, setup);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ehci_timeout, xfer);
+ }
+ ehci_add_intr_list(sc, exfer);
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 10) {
+ DPRINTF(("ehci_device_request: status=%x\n",
+ EOREAD4(sc, EHCI_USBSTS)));
+ delay(10000);
+ ehci_dump_regs(sc);
+ ehci_dump_sqh(sc, sc->sc_async_head);
+ ehci_dump_sqh(sc, sqh);
+ ehci_dump_sqtds(sc, setup);
+ }
+#endif
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad3:
+ sqh->inactivesqtd = setup;
+ ehci_free_sqtd(sc, stat);
+ bad2:
+ ehci_free_sqtd(sc, newinactive);
+ bad1:
+ DPRINTFN(-1,("ehci_device_request: no memory\n"));
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return (err);
+#undef exfer
+}
+
+/************************/
+
+static usbd_status
+ehci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ehci_device_bulk_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+ehci_device_bulk_start(usbd_xfer_handle xfer)
+{
+#define exfer EXFER(xfer)
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ usbd_device_handle dev = epipe->pipe.device;
+ ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
+ ehci_soft_qtd_t *data, *dataend, *newinactive;
+ ehci_soft_qh_t *sqh;
+ usbd_status err;
+ int len, isread, endpt;
+ int s;
+
+ DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%d flags=%d\n",
+ xfer, xfer->length, xfer->flags));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("ehci_device_bulk_start: a request");
+#endif
+
+ len = xfer->length;
+ endpt = epipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sqh = epipe->sqh;
+
+ epipe->u.bulk.length = len;
+
+ newinactive = ehci_alloc_sqtd(sc);
+ if (newinactive == NULL) {
+ DPRINTFN(-1,("ehci_device_bulk_start: no sqtd memory\n"));
+ err = USBD_NOMEM;
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return (err);
+ }
+ newinactive->qtd.qtd_status = htohc32(sc, 0);
+ newinactive->qtd.qtd_next = EHCI_NULL(sc);
+ newinactive->qtd.qtd_altnext = EHCI_NULL(sc);
+ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
+ sqh->inactivesqtd, newinactive, &data, &dataend);
+ if (err) {
+ DPRINTFN(-1,("ehci_device_bulk_start: no memory\n"));
+ ehci_free_sqtd(sc, newinactive);
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return (err);
+ }
+ dataend->nextqtd = newinactive;
+ dataend->qtd.qtd_next = htohc32(sc, newinactive->physaddr);
+ dataend->qtd.qtd_altnext = htohc32(sc, newinactive->physaddr);
+ sqh->inactivesqtd = newinactive;
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 5) {
+ DPRINTF(("ehci_device_bulk_start: data(1)\n"));
+ ehci_dump_sqh(sc, sqh);
+ ehci_dump_sqtds(sc, data);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ exfer->sqtdstart = data;
+ exfer->sqtdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!exfer->isdone) {
+ printf("ehci_device_bulk_start: not done, ex=%p\n", exfer);
+ }
+ exfer->isdone = 0;
+#endif
+
+ s = splusb();
+ ehci_activate_qh(sc, sqh, data);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ehci_timeout, xfer);
+ }
+ ehci_add_intr_list(sc, exfer);
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 10) {
+ DPRINTF(("ehci_device_bulk_start: data(2)\n"));
+ delay(10000);
+ DPRINTF(("ehci_device_bulk_start: data(3)\n"));
+ ehci_dump_regs(sc);
+#if 0
+ printf("async_head:\n");
+ ehci_dump_sqh(sc->sc_async_head);
+#endif
+ printf("sqh:\n");
+ ehci_dump_sqh(sc, sqh);
+ ehci_dump_sqtds(sc, data);
+ }
+#endif
+
+ if (sc->sc_bus.use_polling)
+ ehci_waitintr(sc, xfer);
+
+ return (USBD_IN_PROGRESS);
+#undef exfer
+}
+
+static void
+ehci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ehci_device_bulk_abort: xfer=%p\n", xfer));
+ ehci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/*
+ * Close a device bulk pipe.
+ */
+static void
+ehci_device_bulk_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ehci_device_bulk_close: pipe=%p\n", pipe));
+ ehci_close_pipe(pipe, sc->sc_async_head);
+}
+
+void
+ehci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ struct ehci_xfer *ex = EXFER(xfer);
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+
+ DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n",
+ xfer, xfer->actlen));
+
+ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
+ ehci_del_intr_list(ex); /* remove from active list */
+ ehci_free_sqtd_chain(sc, epipe->sqh, ex->sqtdstart,
+ ex->sqtdend->nextqtd);
+ }
+
+ DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen));
+}
+
+/************************/
+
+static usbd_status
+ehci_device_setintr(ehci_softc_t *sc, ehci_soft_qh_t *sqh, int ival)
+{
+ struct ehci_soft_islot *isp;
+ int islot, lev;
+
+ /* Find a poll rate that is large enough. */
+ for (lev = EHCI_IPOLLRATES - 1; lev > 0; lev--)
+ if (EHCI_ILEV_IVAL(lev) <= ival)
+ break;
+
+ /* Pick an interrupt slot at the right level. */
+ /* XXX could do better than picking at random. */
+ islot = EHCI_IQHIDX(lev, arc4random());
+
+ sqh->islot = islot;
+ isp = &sc->sc_islots[islot];
+ ehci_add_qh(sc, sqh, isp->sqh);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static usbd_status
+ehci_device_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (ehci_device_intr_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ehci_device_intr_start(usbd_xfer_handle xfer)
+{
+#define exfer EXFER(xfer)
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ usbd_device_handle dev = xfer->pipe->device;
+ ehci_softc_t *sc = (ehci_softc_t *)dev->bus;
+ ehci_soft_qtd_t *data, *dataend, *newinactive;
+ ehci_soft_qh_t *sqh;
+ usbd_status err;
+ int len, isread, endpt;
+ int s;
+
+ DPRINTFN(2, ("ehci_device_intr_start: xfer=%p len=%d flags=%d\n",
+ xfer, xfer->length, xfer->flags));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("ehci_device_intr_start: a request");
+#endif
+
+ len = xfer->length;
+ endpt = epipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sqh = epipe->sqh;
+
+ epipe->u.intr.length = len;
+
+ newinactive = ehci_alloc_sqtd(sc);
+ if (newinactive == NULL) {
+ DPRINTFN(-1,("ehci_device_intr_start: no sqtd memory\n"));
+ err = USBD_NOMEM;
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return (err);
+ }
+ newinactive->qtd.qtd_status = htohc32(sc, 0);
+ newinactive->qtd.qtd_next = EHCI_NULL(sc);
+ newinactive->qtd.qtd_altnext = EHCI_NULL(sc);
+ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
+ sqh->inactivesqtd, newinactive, &data, &dataend);
+ if (err) {
+ DPRINTFN(-1, ("ehci_device_intr_start: no memory\n"));
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return (err);
+ }
+ dataend->nextqtd = newinactive;
+ dataend->qtd.qtd_next = htohc32(sc, newinactive->physaddr);
+ dataend->qtd.qtd_altnext = htohc32(sc, newinactive->physaddr);
+ sqh->inactivesqtd = newinactive;
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 5) {
+ DPRINTF(("ehci_device_intr_start: data(1)\n"));
+ ehci_dump_sqh(sc, sqh);
+ ehci_dump_sqtds(sc, data);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ exfer->sqtdstart = data;
+ exfer->sqtdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!exfer->isdone) {
+ printf("ehci_device_intr_start: not done, ex=%p\n", exfer);
+ }
+ exfer->isdone = 0;
+#endif
+
+ s = splusb();
+ ehci_activate_qh(sc, sqh, data);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ehci_timeout, xfer);
+ }
+ ehci_add_intr_list(sc, exfer);
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef EHCI_DEBUG
+ if (ehcidebug > 10) {
+ DPRINTF(("ehci_device_intr_start: data(2)\n"));
+ delay(10000);
+ DPRINTF(("ehci_device_intr_start: data(3)\n"));
+ ehci_dump_regs(sc);
+ printf("sqh:\n");
+ ehci_dump_sqh(sc, sqh);
+ ehci_dump_sqtds(sc, data);
+ }
+#endif
+
+ if (sc->sc_bus.use_polling)
+ ehci_waitintr(sc, xfer);
+
+ return (USBD_IN_PROGRESS);
+#undef exfer
+}
+
+static void
+ehci_device_intr_abort(usbd_xfer_handle xfer)
+{
+ DPRINTFN(1, ("ehci_device_intr_abort: xfer=%p\n", xfer));
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTFN(1, ("ehci_device_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ /*
+ * XXX - abort_xfer uses ehci_sync_hc, which syncs via the advance
+ * async doorbell. That's dependant on the async list, wheras
+ * intr xfers are periodic, should not use this?
+ */
+ ehci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+static void
+ehci_device_intr_close(usbd_pipe_handle pipe)
+{
+ ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
+ struct ehci_soft_islot *isp;
+
+ isp = &sc->sc_islots[epipe->sqh->islot];
+ ehci_close_pipe(pipe, isp->sqh);
+}
+
+static void
+ehci_device_intr_done(usbd_xfer_handle xfer)
+{
+#define exfer EXFER(xfer)
+ struct ehci_xfer *ex = EXFER(xfer);
+ ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
+ ehci_soft_qtd_t *data, *dataend, *newinactive;
+ ehci_soft_qh_t *sqh;
+ usbd_status err;
+ int len, isread, endpt, s;
+
+ DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n",
+ xfer, xfer->actlen));
+
+ sqh = epipe->sqh;
+ if (xfer->pipe->repeat) {
+ ehci_free_sqtd_chain(sc, sqh, ex->sqtdstart,
+ ex->sqtdend->nextqtd);
+
+ len = epipe->u.intr.length;
+ xfer->length = len;
+ endpt = epipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+
+ newinactive = ehci_alloc_sqtd(sc);
+ if (newinactive == NULL) {
+ DPRINTFN(-1,
+ ("ehci_device_intr_done: no sqtd memory\n"));
+ err = USBD_NOMEM;
+ xfer->status = err;
+ return;
+ }
+ newinactive->qtd.qtd_status = htohc32(sc, 0);
+ newinactive->qtd.qtd_next = EHCI_NULL(sc);
+ newinactive->qtd.qtd_altnext = EHCI_NULL(sc);
+ err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
+ sqh->inactivesqtd, newinactive, &data, &dataend);
+ if (err) {
+ DPRINTFN(-1, ("ehci_device_intr_done: no memory\n"));
+ xfer->status = err;
+ return;
+ }
+ dataend->nextqtd = newinactive;
+ dataend->qtd.qtd_next = htohc32(sc, newinactive->physaddr);
+ dataend->qtd.qtd_altnext = htohc32(sc, newinactive->physaddr);
+ sqh->inactivesqtd = newinactive;
+
+ /* Set up interrupt info. */
+ exfer->sqtdstart = data;
+ exfer->sqtdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!exfer->isdone) {
+ printf("ehci_device_intr_done: not done, ex=%p\n",
+ exfer);
+ }
+ exfer->isdone = 0;
+#endif
+
+ s = splusb();
+ ehci_activate_qh(sc, sqh, data);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle,
+ MS_TO_TICKS(xfer->timeout), ehci_timeout, xfer);
+ }
+ splx(s);
+
+ xfer->status = USBD_IN_PROGRESS;
+ } else if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
+ ehci_del_intr_list(ex); /* remove from active list */
+ ehci_free_sqtd_chain(sc, sqh, ex->sqtdstart,
+ ex->sqtdend->nextqtd);
+ }
+#undef exfer
+}
+
+/************************/
+
+static usbd_status
+ehci_device_isoc_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ err = usb_insert_transfer(xfer);
+ if (err && err != USBD_IN_PROGRESS)
+ return (err);
+
+ return (ehci_device_isoc_start(xfer));
+}
+
+static usbd_status
+ehci_device_isoc_start(usbd_xfer_handle xfer)
+{
+ struct ehci_pipe *epipe;
+ usbd_device_handle dev;
+ ehci_softc_t *sc;
+ struct ehci_xfer *exfer;
+ ehci_soft_itd_t *itd, *prev, *start, *stop;
+ usb_dma_t *dma_buf;
+ int i, j, k, frames, uframes, ufrperframe;
+ int s, trans_count, offs, total_length;
+ int frindex;
+
+ start = NULL;
+ prev = NULL;
+ itd = NULL;
+ trans_count = 0;
+ total_length = 0;
+ exfer = (struct ehci_xfer *) xfer;
+ sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ dev = xfer->pipe->device;
+ epipe = (struct ehci_pipe *)xfer->pipe;
+
+ /*
+ * To allow continuous transfers, above we start all transfers
+ * immediately. However, we're still going to get usbd_start_next call
+ * this when another xfer completes. So, check if this is already
+ * in progress or not
+ */
+
+ if (exfer->itdstart != NULL)
+ return (USBD_IN_PROGRESS);
+
+ DPRINTFN(2, ("ehci_device_isoc_start: xfer %p len %d flags %d\n",
+ xfer, xfer->length, xfer->flags));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ /*
+ * To avoid complication, don't allow a request right now that'll span
+ * the entire frame table. To within 4 frames, to allow some leeway
+ * on either side of where the hc currently is.
+ */
+ if ((1 << (epipe->pipe.endpoint->edesc->bInterval)) *
+ xfer->nframes >= (sc->sc_flsize - 4) * 8) {
+ printf("ehci: isoc descriptor requested that spans the entire"
+ " frametable, too many frames\n");
+ return (USBD_INVAL);
+ }
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("ehci_device_isoc_start: request\n");
+
+ if (!exfer->isdone)
+ printf("ehci_device_isoc_start: not done, ex = %p\n", exfer);
+ exfer->isdone = 0;
+#endif
+
+ /*
+ * Step 1: Allocate and initialize itds, how many do we need?
+ * One per transfer if interval >= 8 microframes, fewer if we use
+ * multiple microframes per frame.
+ */
+
+ i = epipe->pipe.endpoint->edesc->bInterval;
+ if (i > 16 || i == 0) {
+ /* Spec page 271 says intervals > 16 are invalid */
+ DPRINTF(("ehci_device_isoc_start: bInvertal %d invalid\n", i));
+ return (USBD_INVAL);
+ }
+
+ switch (i) {
+ case 1:
+ ufrperframe = 8;
+ break;
+ case 2:
+ ufrperframe = 4;
+ break;
+ case 3:
+ ufrperframe = 2;
+ break;
+ default:
+ ufrperframe = 1;
+ break;
+ }
+ frames = (xfer->nframes + (ufrperframe - 1)) / ufrperframe;
+ uframes = 8 / ufrperframe;
+
+ if (frames == 0) {
+ DPRINTF(("ehci_device_isoc_start: frames == 0\n"));
+ return (USBD_INVAL);
+ }
+
+ dma_buf = xfer->buffer;
+ offs = 0;
+
+ for (i = 0; i < frames; i++) {
+ int froffs = offs;
+ itd = ehci_alloc_itd(sc);
+
+ if (prev != NULL) {
+ prev->itd.itd_next =
+ htohc32(sc, itd->physaddr | EHCI_LINK_ITD);
+ prev->xfer_next = itd;
+ } else {
+ start = itd;
+ }
+
+ /*
+ * Step 1.5, initialize uframes
+ */
+ for (j = 0; j < 8; j += uframes) {
+ /* Calculate which page in the list this starts in */
+ int addr = DMAADDR(dma_buf, froffs);
+ addr = EHCI_PAGE_OFFSET(addr);
+ addr += (offs - froffs);
+ addr = EHCI_PAGE(addr);
+ addr /= EHCI_PAGE_SIZE;
+
+ /* This gets the initial offset into the first page,
+ * looks how far further along the current uframe
+ * offset is. Works out how many pages that is.
+ */
+
+ itd->itd.itd_ctl[j] = htohc32(sc, EHCI_ITD_ACTIVE |
+ EHCI_ITD_SET_LEN(xfer->frlengths[trans_count]) |
+ EHCI_ITD_SET_PG(addr) |
+ EHCI_ITD_SET_OFFS(EHCI_PAGE_OFFSET(DMAADDR(dma_buf,
+ offs))));
+
+ total_length += xfer->frlengths[trans_count];
+ offs += xfer->frlengths[trans_count];
+ trans_count++;
+
+ if (trans_count >= xfer->nframes) { /*Set IOC*/
+ itd->itd.itd_ctl[j] |= htohc32(sc, EHCI_ITD_IOC);
+ }
+ }
+
+ /* Step 1.75, set buffer pointers. To simplify matters, all
+ * pointers are filled out for the next 7 hardware pages in
+ * the dma block, so no need to worry what pages to cover
+ * and what to not.
+ */
+
+ for (j=0; j < 7; j++) {
+ /*
+ * Don't try to lookup a page that's past the end
+ * of buffer
+ */
+ int page_offs = EHCI_PAGE(froffs +
+ (EHCI_PAGE_SIZE * j));
+ if (page_offs >= dma_buf->block->size)
+ break;
+
+ int page = DMAADDR(dma_buf, page_offs);
+ page = EHCI_PAGE(page);
+ itd->itd.itd_bufr[j] =
+ htohc32(sc, EHCI_ITD_SET_BPTR(page) | EHCI_LINK_ITD);
+ }
+
+ /*
+ * Other special values
+ */
+
+ k = epipe->pipe.endpoint->edesc->bEndpointAddress;
+ itd->itd.itd_bufr[0] |= htohc32(sc,
+ EHCI_ITD_SET_EP(UE_GET_ADDR(k)) |
+ EHCI_ITD_SET_DADDR(epipe->pipe.device->address));
+
+ k = (UE_GET_DIR(epipe->pipe.endpoint->edesc->bEndpointAddress))
+ ? 1 : 0;
+ j = UE_GET_SIZE(
+ UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize));
+ itd->itd.itd_bufr[1] |= htohc32(sc, EHCI_ITD_SET_DIR(k) |
+ EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j)));
+
+ /* FIXME: handle invalid trans */
+ itd->itd.itd_bufr[2] |=
+ htohc32(sc, EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1));
+ prev = itd;
+ } /* End of frame */
+
+ stop = itd;
+ stop->xfer_next = NULL;
+ exfer->isoc_len = total_length;
+
+ /*
+ * Part 2: Transfer descriptors have now been set up, now they must
+ * be scheduled into the period frame list. Erk. Not wanting to
+ * complicate matters, transfer is denied if the transfer spans
+ * more than the period frame list.
+ */
+
+ s = splusb();
+
+ /* Start inserting frames */
+ if (epipe->u.isoc.cur_xfers > 0) {
+ frindex = epipe->u.isoc.next_frame;
+ } else {
+ frindex = EOREAD4(sc, EHCI_FRINDEX);
+ frindex = frindex >> 3; /* Erase microframe index */
+ frindex += 2;
+ }
+
+ if (frindex >= sc->sc_flsize)
+ frindex &= (sc->sc_flsize - 1);
+
+ /* Whats the frame interval? */
+ i = (1 << epipe->pipe.endpoint->edesc->bInterval);
+ if (i / 8 == 0)
+ i = 1;
+ else
+ i /= 8;
+
+ itd = start;
+ for (j = 0; j < frames; j++) {
+ if (itd == NULL)
+ panic("ehci: unexpectedly ran out of isoc itds,"
+ "isoc_start\n");
+
+ itd->itd.itd_next = sc->sc_flist[frindex];
+ if (itd->itd.itd_next == 0)
+ /* FIXME: frindex table gets initialized to NULL
+ * or EHCI_NULL? */
+ itd->itd.itd_next = EHCI_NULL(sc);
+
+ sc->sc_flist[frindex] = htohc32(sc, EHCI_LINK_ITD | itd->physaddr);
+
+ itd->u.frame_list.next = sc->sc_softitds[frindex];
+ sc->sc_softitds[frindex] = itd;
+ if (itd->u.frame_list.next != NULL)
+ itd->u.frame_list.next->u.frame_list.prev = itd;
+ itd->slot = frindex;
+ itd->u.frame_list.prev = NULL;
+
+ frindex += i;
+ if (frindex >= sc->sc_flsize)
+ frindex -= sc->sc_flsize;
+
+ itd = itd->xfer_next;
+ }
+
+ epipe->u.isoc.cur_xfers++;
+ epipe->u.isoc.next_frame = frindex;
+
+ exfer->itdstart = start;
+ exfer->itdend = stop;
+ exfer->sqtdstart = NULL;
+ exfer->sqtdstart = NULL;
+
+ ehci_add_intr_list(sc, exfer);
+ xfer->status = USBD_IN_PROGRESS;
+ xfer->done = 0;
+ splx(s);
+
+ if (sc->sc_bus.use_polling) {
+ printf("Starting ehci isoc xfer with polling. Bad idea?\n");
+ ehci_waitintr(sc, xfer);
+ }
+
+ return (USBD_IN_PROGRESS);
+}
+
+static void
+ehci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+ DPRINTFN(1, ("ehci_device_isoc_abort: xfer = %p\n", xfer));
+ ehci_abort_isoc_xfer(xfer, USBD_CANCELLED);
+}
+
+static void
+ehci_device_isoc_close(usbd_pipe_handle pipe)
+{
+ printf("ehci_device_isoc_close: nothing in the pipe to free?\n");
+}
+
+static void
+ehci_device_isoc_done(usbd_xfer_handle xfer)
+{
+ struct ehci_xfer *exfer;
+ ehci_softc_t *sc;
+ struct ehci_pipe *epipe;
+ int s;
+
+ exfer = EXFER(xfer);
+ sc = (ehci_softc_t *)xfer->pipe->device->bus;
+ epipe = (struct ehci_pipe *) xfer->pipe;
+
+ s = splusb();
+ epipe->u.isoc.cur_xfers--;
+ if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
+ ehci_del_intr_list(exfer);
+ ehci_rem_free_itd_chain(sc, exfer);
+ }
+ splx(s);
+}
diff --git a/sys/legacy/dev/usb/ehci_ddb.c b/sys/legacy/dev/usb/ehci_ddb.c
new file mode 100644
index 0000000..3ebd130
--- /dev/null
+++ b/sys/legacy/dev/usb/ehci_ddb.c
@@ -0,0 +1,255 @@
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/lockmgr.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+
+#include <dev/usb/ehcireg.h>
+#include <dev/usb/ehcivar.h>
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#include <ddb/db_sym.h>
+#else
+#define db_printf printf
+#endif
+
+extern ehci_softc_t *theehci; /* XXX */
+
+void
+ehci_dump_regs(ehci_softc_t *sc)
+{
+ int i;
+ db_printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n",
+ EOREAD4(sc, EHCI_USBCMD),
+ EOREAD4(sc, EHCI_USBSTS),
+ EOREAD4(sc, EHCI_USBINTR));
+ db_printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n",
+ EOREAD4(sc, EHCI_FRINDEX),
+ EOREAD4(sc, EHCI_CTRLDSSEGMENT),
+ EOREAD4(sc, EHCI_PERIODICLISTBASE),
+ EOREAD4(sc, EHCI_ASYNCLISTADDR));
+ for (i = 1; i <= sc->sc_noport; i++)
+ db_printf("port %d status=0x%08x\n", i,
+ EOREAD4(sc, EHCI_PORTSC(i)));
+}
+
+static void
+ehci_dump_link(ehci_softc_t *sc, ehci_link_t link, int type)
+{
+ link = hc32toh(sc, link);
+ db_printf("0x%08x", link);
+ if (link & EHCI_LINK_TERMINATE)
+ db_printf("<T>");
+ else {
+ db_printf("<");
+ if (type) {
+ switch (EHCI_LINK_TYPE(link)) {
+ case EHCI_LINK_ITD: db_printf("ITD"); break;
+ case EHCI_LINK_QH: db_printf("QH"); break;
+ case EHCI_LINK_SITD: db_printf("SITD"); break;
+ case EHCI_LINK_FSTN: db_printf("FSTN"); break;
+ }
+ }
+ db_printf(">");
+ }
+}
+
+void
+ehci_dump_sqtds(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
+{
+ int i;
+ u_int32_t stop;
+
+ stop = 0;
+ for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) {
+ ehci_dump_sqtd(sc, sqtd);
+ stop = sqtd->qtd.qtd_next & htohc32(sc, EHCI_LINK_TERMINATE);
+ }
+ if (sqtd)
+ db_printf("dump aborted, too many TDs\n");
+}
+
+void
+ehci_dump_qtd(ehci_softc_t *sc, ehci_qtd_t *qtd)
+{
+ u_int32_t s;
+
+ db_printf(" next="); ehci_dump_link(sc, qtd->qtd_next, 0);
+ db_printf(" altnext="); ehci_dump_link(sc, qtd->qtd_altnext, 0);
+ db_printf("\n");
+ s = hc32toh(sc, qtd->qtd_status);
+ db_printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n",
+ s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s),
+ EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s));
+ db_printf(" cerr=%d pid=%d stat=%b\n", EHCI_QTD_GET_CERR(s),
+ EHCI_QTD_GET_PID(s),
+ EHCI_QTD_GET_STATUS(s), EHCI_QTD_STATUS_BITS);
+ for (s = 0; s < 5; s++)
+ db_printf(" buffer[%d]=0x%08x\n", s, hc32toh(sc, qtd->qtd_buffer[s]));
+}
+
+void
+ehci_dump_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
+{
+ db_printf("QTD(%p) at 0x%08x:\n", sqtd, sqtd->physaddr);
+ ehci_dump_qtd(sc, &sqtd->qtd);
+}
+
+void
+ehci_dump_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh)
+{
+ ehci_qh_t *qh = &sqh->qh;
+ u_int32_t endp, endphub;
+
+ db_printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr);
+ db_printf(" sqtd=%p inactivesqtd=%p\n", sqh->sqtd, sqh->inactivesqtd);
+ db_printf(" link="); ehci_dump_link(sc, qh->qh_link, 1); db_printf("\n");
+ endp = hc32toh(sc, qh->qh_endp);
+ db_printf(" endp=0x%08x\n", endp);
+ db_printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n",
+ EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp),
+ EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp),
+ EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp));
+ db_printf(" mpl=0x%x ctl=%d nrl=%d\n",
+ EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp),
+ EHCI_QH_GET_NRL(endp));
+ endphub = hc32toh(sc, qh->qh_endphub);
+ db_printf(" endphub=0x%08x\n", endphub);
+ db_printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n",
+ EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub),
+ EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub),
+ EHCI_QH_GET_MULT(endphub));
+ db_printf(" curqtd="); ehci_dump_link(sc, qh->qh_curqtd, 0); db_printf("\n");
+ db_printf("Overlay qTD:\n");
+ ehci_dump_qtd(sc, &qh->qh_qtd);
+}
+
+void
+ehci_dump_itd(ehci_softc_t *sc, struct ehci_soft_itd *itd)
+{
+ ehci_isoc_trans_t t;
+ ehci_isoc_bufr_ptr_t b, b2, b3;
+ int i;
+
+ db_printf("ITD: next phys=%X\n", itd->itd.itd_next);
+
+ for (i = 0; i < 8;i++) {
+ t = hc32toh(sc, itd->itd.itd_ctl[i]);
+ db_printf("ITDctl %d: stat=%X len=%X ioc=%X pg=%X offs=%X\n", i,
+ EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t),
+ EHCI_ITD_GET_IOC(t), EHCI_ITD_GET_PG(t),
+ EHCI_ITD_GET_OFFS(t));
+ }
+ db_printf("ITDbufr: ");
+ for (i = 0; i < 7; i++)
+ db_printf("%X,", EHCI_ITD_GET_BPTR(hc32toh(sc, itd->itd.itd_bufr[i])));
+
+ b = hc32toh(sc, itd->itd.itd_bufr[0]);
+ b2 = hc32toh(sc, itd->itd.itd_bufr[1]);
+ b3 = hc32toh(sc, itd->itd.itd_bufr[2]);
+ db_printf("\nep=%X daddr=%X dir=%d maxpkt=%X multi=%X\n",
+ EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2),
+ EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3));
+}
+
+void
+ehci_dump_sitd(ehci_softc_t *sc, struct ehci_soft_itd *itd)
+{
+ db_printf("SITD %p next=%p prev=%p xfernext=%p physaddr=%X slot=%d\n",
+ itd, itd->u.frame_list.next, itd->u.frame_list.prev,
+ itd->xfer_next, itd->physaddr, itd->slot);
+}
+
+void
+ehci_dump_exfer(struct ehci_xfer *ex)
+{
+#ifdef DIAGNOSTIC
+ db_printf("%p: sqtdstart %p end %p itdstart %p end %p isdone %d\n",
+ ex, ex->sqtdstart, ex->sqtdend, ex->itdstart,
+ ex->itdend, ex->isdone);
+#else
+ db_printf("%p: sqtdstart %p end %p itdstart %p end %p\n",
+ ex, ex->sqtdstart, ex->sqtdend, ex->itdstart, ex->itdend);
+#endif
+}
+
+#ifdef DDB
+DB_SHOW_COMMAND(ehci, db_show_ehci)
+{
+ if (!have_addr) {
+ db_printf("usage: show ehci <addr>\n");
+ return;
+ }
+ ehci_dump_regs((ehci_softc_t *) addr);
+}
+
+DB_SHOW_COMMAND(ehci_sqtds, db_show_ehci_sqtds)
+{
+ if (!have_addr) {
+ db_printf("usage: show ehci_sqtds <addr>\n");
+ return;
+ }
+ ehci_dump_sqtds(theehci, (ehci_soft_qtd_t *) addr);
+}
+
+DB_SHOW_COMMAND(ehci_qtd, db_show_ehci_qtd)
+{
+ if (!have_addr) {
+ db_printf("usage: show ehci_qtd <addr>\n");
+ return;
+ }
+ ehci_dump_qtd(theehci, (ehci_qtd_t *) addr);
+}
+
+DB_SHOW_COMMAND(ehci_sqh, db_show_ehci_sqh)
+{
+ if (!have_addr) {
+ db_printf("usage: show ehci_sqh <addr>\n");
+ return;
+ }
+ ehci_dump_sqh(theehci, (ehci_soft_qh_t *) addr);
+}
+
+DB_SHOW_COMMAND(ehci_itd, db_show_ehci_itd)
+{
+ if (!have_addr) {
+ db_printf("usage: show ehci_itd <addr>\n");
+ return;
+ }
+ ehci_dump_itd(theehci, (struct ehci_soft_itd *) addr);
+}
+
+DB_SHOW_COMMAND(ehci_sitd, db_show_ehci_sitd)
+{
+ if (!have_addr) {
+ db_printf("usage: show ehci_sitd <addr>\n");
+ return;
+ }
+ ehci_dump_itd(theehci, (struct ehci_soft_itd *) addr);
+}
+
+DB_SHOW_COMMAND(ehci_xfer, db_show_ehci_xfer)
+{
+ if (!have_addr) {
+ db_printf("usage: show ehci_xfer <addr>\n");
+ return;
+ }
+ ehci_dump_exfer((struct ehci_xfer *) addr);
+}
+#endif /* DDB */
diff --git a/sys/legacy/dev/usb/ehci_ixp4xx.c b/sys/legacy/dev/usb/ehci_ixp4xx.c
new file mode 100644
index 0000000..bc800d3
--- /dev/null
+++ b/sys/legacy/dev/usb/ehci_ixp4xx.c
@@ -0,0 +1,360 @@
+/*-
+ * Copyright (c) 2008 Sam Leffler. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * IXP435 attachment driver for the USB Enhanced Host Controller.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/lockmgr.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/ehcireg.h>
+#include <dev/usb/ehcivar.h>
+
+#include <arm/xscale/ixp425/ixp425reg.h>
+#include <arm/xscale/ixp425/ixp425var.h>
+
+#define EHCI_VENDORID_IXP4XX 0x42fa05
+#define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller"
+
+struct ixp_ehci_softc {
+ ehci_softc_t base; /* storage for EHCI code */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ struct bus_space tag; /* tag for private bus space ops */
+};
+
+static int ehci_ixp_detach(device_t self);
+
+static uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t);
+static void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t);
+static uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t);
+static void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t);
+static uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t);
+static void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t);
+
+static int
+ehci_ixp_suspend(device_t self)
+{
+ ehci_softc_t *sc;
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err == 0) {
+ sc = device_get_softc(self);
+ ehci_power(PWR_SUSPEND, sc);
+ }
+ return err;
+}
+
+static int
+ehci_ixp_resume(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ ehci_power(PWR_RESUME, sc);
+ bus_generic_resume(self);
+ return 0;
+}
+
+static int
+ehci_ixp_shutdown(device_t self)
+{
+ ehci_softc_t *sc;
+ int err;
+
+ err = bus_generic_shutdown(self);
+ if (err == 0) {
+ sc = device_get_softc(self);
+ ehci_shutdown(sc);
+ }
+ return err;
+}
+
+static int
+ehci_ixp_probe(device_t self)
+{
+ device_set_desc(self, EHCI_HC_DEVSTR);
+ return BUS_PROBE_DEFAULT;
+}
+
+static int
+ehci_ixp_attach(device_t self)
+{
+ struct ixp_ehci_softc *isc = device_get_softc(self);
+ ehci_softc_t *sc = &isc->base;
+ int err, rid;
+
+ sc->sc_bus.usbrev = USBREV_2_0;
+
+ /* NB: hints fix the memory location and irq */
+
+ rid = 0;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->io_res == NULL) {
+ device_printf(self, "Could not map memory\n");
+ return ENXIO;
+ }
+
+ /*
+ * Craft special resource for bus space ops that handle
+ * byte-alignment of non-word addresses. Also, since
+ * we're already intercepting bus space ops we handle
+ * the register window offset that could otherwise be
+ * done with bus_space_subregion.
+ */
+ isc->iot = rman_get_bustag(sc->io_res);
+ isc->tag.bs_cookie = isc->iot;
+ /* read single */
+ isc->tag.bs_r_1 = ehci_bs_r_1,
+ isc->tag.bs_r_2 = ehci_bs_r_2,
+ isc->tag.bs_r_4 = ehci_bs_r_4,
+ /* write (single) */
+ isc->tag.bs_w_1 = ehci_bs_w_1,
+ isc->tag.bs_w_2 = ehci_bs_w_2,
+ isc->tag.bs_w_4 = ehci_bs_w_4,
+
+ sc->iot = &isc->tag;
+ sc->ioh = rman_get_bushandle(sc->io_res);
+ sc->sc_size = IXP435_USB1_SIZE - 0x100;
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ,
+ &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ ehci_ixp_detach(self);
+ return ENXIO;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ ehci_ixp_detach(self);
+ return ENOMEM;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ sprintf(sc->sc_vendor, "Intel");
+ sc->sc_id_vendor = EHCI_VENDORID_IXP4XX;
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
+ NULL, (driver_intr_t*)ehci_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ ehci_ixp_detach(self);
+ return ENXIO;
+ }
+
+ /* There are no companion USB controllers */
+ sc->sc_ncomp = 0;
+
+ /* Allocate a parent dma tag for DMA maps */
+ err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ NULL, NULL, &sc->sc_bus.parent_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate parent DMA tag (%d)\n",
+ err);
+ ehci_ixp_detach(self);
+ return ENXIO;
+ }
+
+ /* Allocate a dma tag for transfer buffers */
+ err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate buffer DMA tag (%d)\n",
+ err);
+ ehci_ixp_detach(self);
+ return ENXIO;
+ }
+
+ /*
+ * Arrange to force Host mode, select big-endian byte alignment,
+ * and arrange to not terminate reset operations (the adapter
+ * will ignore it if we do but might as well save a reg write).
+ * Also, the controller has an embedded Transaction Translator
+ * which means port speed must be read from the Port Status
+ * register following a port enable.
+ */
+ sc->sc_flags |= EHCI_SCFLG_TT
+ | EHCI_SCFLG_SETMODE
+ | EHCI_SCFLG_BIGEDESC
+ | EHCI_SCFLG_BIGEMMIO
+ | EHCI_SCFLG_NORESTERM
+ ;
+ (void) ehci_reset(sc);
+
+ err = ehci_init(sc);
+ if (!err) {
+ sc->sc_flags |= EHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ ehci_ixp_detach(self);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ehci_ixp_detach(device_t self)
+{
+ struct ixp_ehci_softc *isc = device_get_softc(self);
+ ehci_softc_t *sc = &isc->base;
+ int err;
+
+ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
+ ehci_detach(sc, 0);
+ sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+ }
+
+ /*
+ * Disable interrupts that might have been switched on in ehci_init()
+ */
+ if (sc->iot && sc->ioh)
+ bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0);
+ if (sc->sc_bus.parent_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.parent_dmatag);
+ if (sc->sc_bus.buffer_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag);
+
+ if (sc->irq_res && sc->ih) {
+ err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ if (sc->sc_bus.bdev != NULL) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res != NULL) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (sc->io_res != NULL) {
+ bus_release_resource(self, SYS_RES_MEMORY, 0, sc->io_res);
+ sc->io_res = NULL;
+ }
+ sc->iot = 0;
+ sc->ioh = 0;
+ return 0;
+}
+
+/*
+ * Bus space accessors for PIO operations.
+ */
+
+static uint8_t
+ehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o)
+{
+ return bus_space_read_1((bus_space_tag_t) t, h,
+ 0x100 + (o &~ 3) + (3 - (o & 3)));
+}
+
+static void
+ehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v)
+{
+ panic("%s", __func__);
+}
+
+static uint16_t
+ehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o)
+{
+ return bus_space_read_2((bus_space_tag_t) t, h,
+ 0x100 + (o &~ 3) + (2 - (o & 3)));
+}
+
+static void
+ehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v)
+{
+ panic("%s", __func__);
+}
+
+static uint32_t
+ehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o)
+{
+ return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o);
+}
+
+static void
+ehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v)
+{
+ bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_ixp_probe),
+ DEVMETHOD(device_attach, ehci_ixp_attach),
+ DEVMETHOD(device_detach, ehci_ixp_detach),
+ DEVMETHOD(device_suspend, ehci_ixp_suspend),
+ DEVMETHOD(device_resume, ehci_ixp_resume),
+ DEVMETHOD(device_shutdown, ehci_ixp_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(struct ixp_ehci_softc),
+};
+static devclass_t ehci_devclass;
+DRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0);
diff --git a/sys/legacy/dev/usb/ehci_mbus.c b/sys/legacy/dev/usb/ehci_mbus.c
new file mode 100644
index 0000000..792c89d
--- /dev/null
+++ b/sys/legacy/dev/usb/ehci_mbus.c
@@ -0,0 +1,396 @@
+/*-
+ * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * 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. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * MBus attachment driver for the USB Enhanced Host Controller.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+#include <sys/lockmgr.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/ehcireg.h>
+#include <dev/usb/ehcivar.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+#define EHCI_VENDORID_MRVL 0x1286
+#define EHCI_HC_DEVSTR "Marvell Integrated USB 2.0 controller"
+
+static device_attach_t ehci_mbus_attach;
+static device_detach_t ehci_mbus_detach;
+static device_shutdown_t ehci_mbus_shutdown;
+static device_suspend_t ehci_mbus_suspend;
+static device_resume_t ehci_mbus_resume;
+
+static int err_intr(void *arg);
+
+struct resource *irq_err;
+void *ih_err;
+
+#define USB_BRIDGE_INTR_CAUSE 0x210
+#define USB_BRIDGE_INTR_MASK 0x214
+
+#define MV_USB_ADDR_DECODE_ERR (1 << 0)
+#define MV_USB_HOST_UNDERFLOW (1 << 1)
+#define MV_USB_HOST_OVERFLOW (1 << 2)
+#define MV_USB_DEVICE_UNDERFLOW (1 << 3)
+
+static int
+ehci_mbus_suspend(device_t self)
+{
+ ehci_softc_t *sc;
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return (err);
+
+ sc = device_get_softc(self);
+ ehci_power(PWR_SUSPEND, sc);
+
+ return (0);
+}
+
+static int
+ehci_mbus_resume(device_t self)
+{
+ ehci_softc_t *sc;
+
+ sc = device_get_softc(self);
+
+ ehci_power(PWR_RESUME, sc);
+ bus_generic_resume(self);
+
+ return (0);
+}
+
+static int
+ehci_mbus_shutdown(device_t self)
+{
+ ehci_softc_t *sc;
+ int err;
+
+ err = bus_generic_shutdown(self);
+ if (err)
+ return (err);
+
+ sc = device_get_softc(self);
+ ehci_shutdown(sc);
+
+ return (0);
+}
+
+static int
+ehci_mbus_probe(device_t self)
+{
+
+ device_set_desc(self, EHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ehci_mbus_attach(device_t self)
+{
+ ehci_softc_t *sc;
+ bus_space_handle_t bsh;
+ int err, rid;
+
+ sc = device_get_softc(self);
+ sc->sc_bus.usbrev = USBREV_2_0;
+
+ rid = 0;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->io_res) {
+ device_printf(self, "Could not map memory\n");
+ return (ENXIO);
+ }
+ sc->iot = rman_get_bustag(sc->io_res);
+ bsh = rman_get_bushandle(sc->io_res);
+
+ /*
+ * Marvell EHCI host controller registers start at certain offset within
+ * the whole USB registers range, so create a subregion for the host
+ * mode configuration purposes.
+ */
+ if (bus_space_subregion(sc->iot, bsh, MV_USB_HOST_OFST,
+ MV_USB_SIZE - MV_USB_HOST_OFST, &sc->ioh) != 0)
+ panic("%s: unable to subregion USB host registers",
+ device_get_name(self));
+ sc->sc_size = MV_USB_SIZE - MV_USB_HOST_OFST;
+
+ rid = 0;
+ irq_err = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (irq_err == NULL) {
+ device_printf(self, "Could not allocate error irq\n");
+ ehci_mbus_detach(self);
+ return (ENXIO);
+ }
+
+ /*
+ * Notice: Marvell EHCI controller has TWO interrupt lines, so make sure to
+ * use the correct rid for the main one (controller interrupt) --
+ * refer to obio_devices[] for the right resource number to use here.
+ */
+ rid = 1;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ ehci_mbus_detach(self);
+ return (ENXIO);
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ ehci_mbus_detach(self);
+ return (ENOMEM);
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ sprintf(sc->sc_vendor, "Marvell");
+ sc->sc_id_vendor = EHCI_VENDORID_MRVL;
+
+ err = bus_setup_intr(self, irq_err, INTR_FAST | INTR_TYPE_BIO,
+ err_intr, NULL, sc, &ih_err);
+ if (err) {
+ device_printf(self, "Could not setup error irq, %d\n", err);
+ ih_err = NULL;
+ ehci_mbus_detach(self);
+ return (ENXIO);
+ }
+
+ EWRITE4(sc, USB_BRIDGE_INTR_MASK, MV_USB_ADDR_DECODE_ERR |
+ MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW |
+ MV_USB_DEVICE_UNDERFLOW);
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
+ NULL, (driver_intr_t*)ehci_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ ehci_mbus_detach(self);
+ return (ENXIO);
+ }
+
+ /* There are no companion USB controllers */
+ sc->sc_ncomp = 0;
+
+ /* Allocate a parent dma tag for DMA maps */
+ err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ NULL, NULL, &sc->sc_bus.parent_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate parent DMA tag (%d)\n",
+ err);
+ ehci_mbus_detach(self);
+ return (ENXIO);
+ }
+
+ /* Allocate a dma tag for transfer buffers */
+ err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate buffer DMA tag (%d)\n",
+ err);
+ ehci_mbus_detach(self);
+ return (ENXIO);
+ }
+
+ /*
+ * Workaround for Marvell integrated EHCI controller: reset of
+ * the EHCI core clears the USBMODE register, which sets the core in
+ * an undefined state (neither host nor agent), so it needs to be set
+ * again for proper operation.
+ *
+ * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for
+ * details.
+ */
+ sc->sc_flags |= EHCI_SCFLG_SETMODE;
+ if (bootverbose)
+ device_printf(self, "5.24 GL USB-2 workaround enabled\n");
+
+ /* XXX all MV chips need it? */
+ sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM;
+
+ err = ehci_init(sc);
+ if (!err) {
+ sc->sc_flags |= EHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ ehci_mbus_detach(self);
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+ehci_mbus_detach(device_t self)
+{
+ ehci_softc_t *sc;
+ int err;
+
+ sc = device_get_softc(self);
+ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
+ ehci_detach(sc, 0);
+ sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+ }
+
+ /*
+ * Disable interrupts that might have been switched on in ehci_init()
+ */
+ if (sc->iot && sc->ioh)
+ bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0);
+ if (sc->sc_bus.parent_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.parent_dmatag);
+ if (sc->sc_bus.buffer_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag);
+
+ if (sc->irq_res && sc->ih) {
+ err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0);
+
+ if (irq_err && ih_err) {
+ err = bus_teardown_intr(self, irq_err, ih_err);
+
+ if (err)
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ ih_err = NULL;
+ }
+
+ if (sc->sc_bus.bdev) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (irq_err) {
+ bus_release_resource(self, SYS_RES_IRQ, 1, irq_err);
+ irq_err = NULL;
+ }
+ if (sc->io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, 0, sc->io_res);
+ sc->io_res = NULL;
+ sc->iot = 0;
+ sc->ioh = 0;
+ }
+ return (0);
+}
+
+static int
+err_intr(void *arg)
+{
+ ehci_softc_t *sc = arg;
+ unsigned int cause;
+
+ cause = EREAD4(sc, USB_BRIDGE_INTR_CAUSE);
+ if (cause) {
+ printf("IRQ ERR: cause: 0x%08x\n", cause);
+ if (cause & MV_USB_ADDR_DECODE_ERR)
+ printf("IRQ ERR: Address decoding error\n");
+ if (cause & MV_USB_HOST_UNDERFLOW)
+ printf("IRQ ERR: USB Host Underflow\n");
+ if (cause & MV_USB_HOST_OVERFLOW)
+ printf("IRQ ERR: USB Host Overflow\n");
+ if (cause & MV_USB_DEVICE_UNDERFLOW)
+ printf("IRQ ERR: USB Device Underflow\n");
+ if (cause & ~(MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW |
+ MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW))
+ printf("IRQ ERR: Unknown error\n");
+
+ EWRITE4(sc, USB_BRIDGE_INTR_CAUSE, 0);
+ }
+ return (FILTER_HANDLED);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_mbus_probe),
+ DEVMETHOD(device_attach, ehci_mbus_attach),
+ DEVMETHOD(device_detach, ehci_mbus_detach),
+ DEVMETHOD(device_suspend, ehci_mbus_suspend),
+ DEVMETHOD(device_resume, ehci_mbus_resume),
+ DEVMETHOD(device_shutdown, ehci_mbus_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(ehci_softc_t),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, mbus, ehci_driver, ehci_devclass, 0, 0);
diff --git a/sys/legacy/dev/usb/ehci_pci.c b/sys/legacy/dev/usb/ehci_pci.c
new file mode 100644
index 0000000..819bc25
--- /dev/null
+++ b/sys/legacy/dev/usb/ehci_pci.c
@@ -0,0 +1,636 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
+ *
+ * The EHCI 1.0 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/docs/usb_20.zip
+ */
+
+/* The low level controller code for EHCI has been split into
+ * PCI probes and EHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+#include <sys/lockmgr.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/ehcireg.h>
+#include <dev/usb/ehcivar.h>
+
+#define PCI_EHCI_VENDORID_ACERLABS 0x10b9
+#define PCI_EHCI_VENDORID_AMD 0x1022
+#define PCI_EHCI_VENDORID_APPLE 0x106b
+#define PCI_EHCI_VENDORID_ATI 0x1002
+#define PCI_EHCI_VENDORID_CMDTECH 0x1095
+#define PCI_EHCI_VENDORID_INTEL 0x8086
+#define PCI_EHCI_VENDORID_NEC 0x1033
+#define PCI_EHCI_VENDORID_OPTI 0x1045
+#define PCI_EHCI_VENDORID_PHILIPS 0x1131
+#define PCI_EHCI_VENDORID_SIS 0x1039
+#define PCI_EHCI_VENDORID_NVIDIA 0x12D2
+#define PCI_EHCI_VENDORID_NVIDIA2 0x10DE
+#define PCI_EHCI_VENDORID_VIA 0x1106
+
+/* AcerLabs/ALi */
+#define PCI_EHCI_DEVICEID_M5239 0x523910b9
+static const char *ehci_device_m5239 = "ALi M5239 USB 2.0 controller";
+
+/* AMD */
+#define PCI_EHCI_DEVICEID_8111 0x10227463
+static const char *ehci_device_8111 = "AMD 8111 USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_CS5536 0x20951022
+static const char *ehci_device_cs5536 = "AMD CS5536 (Geode) USB 2.0 controller";
+
+/* ATI */
+#define PCI_EHCI_DEVICEID_SB200 0x43451002
+static const char *ehci_device_sb200 = "ATI SB200 USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_SB400 0x43731002
+static const char *ehci_device_sb400 = "ATI SB400 USB 2.0 controller";
+
+/* Intel */
+#define PCI_EHCI_DEVICEID_6300 0x25ad8086
+static const char *ehci_device_6300 = "Intel 6300ESB USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_ICH4 0x24cd8086
+static const char *ehci_device_ich4 = "Intel 82801DB/L/M (ICH4) USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_ICH5 0x24dd8086
+static const char *ehci_device_ich5 = "Intel 82801EB/R (ICH5) USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_ICH6 0x265c8086
+static const char *ehci_device_ich6 = "Intel 82801FB (ICH6) USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_ICH7 0x27cc8086
+static const char *ehci_device_ich7 = "Intel 82801GB/R (ICH7) USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_ICH8_A 0x28368086
+static const char *ehci_device_ich8_a = "Intel 82801H (ICH8) USB 2.0 controller USB2-A";
+#define PCI_EHCI_DEVICEID_ICH8_B 0x283a8086
+static const char *ehci_device_ich8_b = "Intel 82801H (ICH8) USB 2.0 controller USB2-B";
+#define PCI_EHCI_DEVICEID_ICH9_A 0x293a8086
+#define PCI_EHCI_DEVICEID_ICH9_B 0x293c8086
+static const char *ehci_device_ich9 = "Intel 82801I (ICH9) USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_63XX 0x268c8086
+static const char *ehci_device_63XX = "Intel 63XXESB USB 2.0 controller";
+
+/* NEC */
+#define PCI_EHCI_DEVICEID_NEC 0x00e01033
+static const char *ehci_device_nec = "NEC uPD 720100 USB 2.0 controller";
+
+/* NVIDIA */
+#define PCI_EHCI_DEVICEID_NF2 0x006810de
+static const char *ehci_device_nf2 = "NVIDIA nForce2 USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_NF2_400 0x008810de
+static const char *ehci_device_nf2_400 = "NVIDIA nForce2 Ultra 400 USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_NF3 0x00d810de
+static const char *ehci_device_nf3 = "NVIDIA nForce3 USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_NF3_250 0x00e810de
+static const char *ehci_device_nf3_250 = "NVIDIA nForce3 250 USB 2.0 controller";
+#define PCI_EHCI_DEVICEID_NF4 0x005b10de
+static const char *ehci_device_nf4 = "NVIDIA nForce4 USB 2.0 controller";
+
+/* Philips */
+#define PCI_EHCI_DEVICEID_ISP156X 0x15621131
+static const char *ehci_device_isp156x = "Philips ISP156x USB 2.0 controller";
+
+#define PCI_EHCI_DEVICEID_VIA 0x31041106
+static const char *ehci_device_via = "VIA VT6202 USB 2.0 controller";
+
+static const char *ehci_device_generic = "EHCI (generic) USB 2.0 controller";
+
+#define PCI_EHCI_BASE_REG 0x10
+
+#ifdef USB_DEBUG
+#define EHCI_DEBUG USB_DEBUG
+#define DPRINTF(x) do { if (ehcidebug) printf x; } while (0)
+extern int ehcidebug;
+#else
+#define DPRINTF(x)
+#endif
+
+static device_attach_t ehci_pci_attach;
+static device_detach_t ehci_pci_detach;
+static device_shutdown_t ehci_pci_shutdown;
+static device_suspend_t ehci_pci_suspend;
+static device_resume_t ehci_pci_resume;
+static void ehci_pci_givecontroller(device_t self);
+static void ehci_pci_takecontroller(device_t self);
+
+static int
+ehci_pci_suspend(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return (err);
+ ehci_power(PWR_SUSPEND, sc);
+
+ return 0;
+}
+
+static int
+ehci_pci_resume(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ ehci_pci_takecontroller(self);
+ ehci_power(PWR_RESUME, sc);
+ bus_generic_resume(self);
+
+ return 0;
+}
+
+static int
+ehci_pci_shutdown(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_shutdown(self);
+ if (err)
+ return (err);
+ ehci_shutdown(sc);
+ ehci_pci_givecontroller(self);
+
+ return 0;
+}
+
+static const char *
+ehci_pci_match(device_t self)
+{
+ u_int32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case PCI_EHCI_DEVICEID_M5239:
+ return (ehci_device_m5239);
+ case PCI_EHCI_DEVICEID_8111:
+ return (ehci_device_8111);
+ case PCI_EHCI_DEVICEID_CS5536:
+ return (ehci_device_cs5536);
+ case PCI_EHCI_DEVICEID_SB200:
+ return (ehci_device_sb200);
+ case PCI_EHCI_DEVICEID_SB400:
+ return (ehci_device_sb400);
+ case PCI_EHCI_DEVICEID_6300:
+ return (ehci_device_6300);
+ case PCI_EHCI_DEVICEID_63XX:
+ return (ehci_device_63XX);
+ case PCI_EHCI_DEVICEID_ICH4:
+ return (ehci_device_ich4);
+ case PCI_EHCI_DEVICEID_ICH5:
+ return (ehci_device_ich5);
+ case PCI_EHCI_DEVICEID_ICH6:
+ return (ehci_device_ich6);
+ case PCI_EHCI_DEVICEID_ICH7:
+ return (ehci_device_ich7);
+ case PCI_EHCI_DEVICEID_ICH8_A:
+ return (ehci_device_ich8_a);
+ case PCI_EHCI_DEVICEID_ICH8_B:
+ return (ehci_device_ich8_b);
+ case PCI_EHCI_DEVICEID_ICH9_A:
+ case PCI_EHCI_DEVICEID_ICH9_B:
+ return (ehci_device_ich9);
+ case PCI_EHCI_DEVICEID_NEC:
+ return (ehci_device_nec);
+ case PCI_EHCI_DEVICEID_NF2:
+ return (ehci_device_nf2);
+ case PCI_EHCI_DEVICEID_NF2_400:
+ return (ehci_device_nf2_400);
+ case PCI_EHCI_DEVICEID_NF3:
+ return (ehci_device_nf3);
+ case PCI_EHCI_DEVICEID_NF3_250:
+ return (ehci_device_nf3_250);
+ case PCI_EHCI_DEVICEID_NF4:
+ return (ehci_device_nf4);
+ case PCI_EHCI_DEVICEID_ISP156X:
+ return (ehci_device_isp156x);
+ case PCI_EHCI_DEVICEID_VIA:
+ return (ehci_device_via);
+ default:
+ if (pci_get_class(self) == PCIC_SERIALBUS
+ && pci_get_subclass(self) == PCIS_SERIALBUS_USB
+ && pci_get_progif(self) == PCI_INTERFACE_EHCI) {
+ return (ehci_device_generic);
+ }
+ }
+
+ return NULL; /* dunno */
+}
+
+static int
+ehci_pci_probe(device_t self)
+{
+ const char *desc = ehci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return BUS_PROBE_DEFAULT;
+ } else {
+ return ENXIO;
+ }
+}
+
+static int
+ehci_pci_attach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ devclass_t dc;
+ device_t parent;
+ device_t *neighbors;
+ device_t *nbus;
+ struct usbd_bus *bsc;
+ int err;
+ int rid;
+ int ncomp;
+ int count, buscount;
+ int slot, function;
+ int res;
+ int i;
+
+ switch(pci_read_config(self, PCI_USBREV, 1) & PCI_USBREV_MASK) {
+ case PCI_USBREV_PRE_1_0:
+ case PCI_USBREV_1_0:
+ case PCI_USBREV_1_1:
+ device_printf(self, "pre-2.0 USB rev\n");
+ if (pci_get_devid(self) == PCI_EHCI_DEVICEID_CS5536) {
+ sc->sc_bus.usbrev = USBREV_2_0;
+ device_printf(self, "Quirk for CS5536 USB 2.0 enabled\n");
+ break;
+ }
+
+ /*
+ * Quirk for Parallels Desktop 4.0.
+ */
+ if (pci_get_devid(self) == PCI_EHCI_DEVICEID_ICH6) {
+ sc->sc_bus.usbrev = USBREV_2_0;
+ break;
+ }
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ return ENXIO;
+ case PCI_USBREV_2_0:
+ sc->sc_bus.usbrev = USBREV_2_0;
+ break;
+ default:
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ break;
+ }
+
+ pci_enable_busmaster(self);
+
+ rid = PCI_CBMEM;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->io_res) {
+ device_printf(self, "Could not map memory\n");
+ return ENXIO;
+ }
+ sc->iot = rman_get_bustag(sc->io_res);
+ sc->ioh = rman_get_bushandle(sc->io_res);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ ehci_pci_detach(self);
+ return ENOMEM;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ /* ehci_pci_match will never return NULL if ehci_pci_probe succeeded */
+ device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ACERLABS:
+ sprintf(sc->sc_vendor, "AcerLabs");
+ break;
+ case PCI_EHCI_VENDORID_AMD:
+ sprintf(sc->sc_vendor, "AMD");
+ break;
+ case PCI_EHCI_VENDORID_APPLE:
+ sprintf(sc->sc_vendor, "Apple");
+ break;
+ case PCI_EHCI_VENDORID_ATI:
+ sprintf(sc->sc_vendor, "ATI");
+ break;
+ case PCI_EHCI_VENDORID_CMDTECH:
+ sprintf(sc->sc_vendor, "CMDTECH");
+ break;
+ case PCI_EHCI_VENDORID_INTEL:
+ sprintf(sc->sc_vendor, "Intel");
+ break;
+ case PCI_EHCI_VENDORID_NEC:
+ sprintf(sc->sc_vendor, "NEC");
+ break;
+ case PCI_EHCI_VENDORID_OPTI:
+ sprintf(sc->sc_vendor, "OPTi");
+ break;
+ case PCI_EHCI_VENDORID_SIS:
+ sprintf(sc->sc_vendor, "SiS");
+ break;
+ case PCI_EHCI_VENDORID_NVIDIA:
+ case PCI_EHCI_VENDORID_NVIDIA2:
+ sprintf(sc->sc_vendor, "nVidia");
+ break;
+ case PCI_EHCI_VENDORID_VIA:
+ sprintf(sc->sc_vendor, "VIA");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New EHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
+ NULL, (driver_intr_t *)ehci_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+
+ /* Enable workaround for dropped interrupts as required */
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ATI:
+ case PCI_EHCI_VENDORID_VIA:
+ sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG;
+ if (bootverbose)
+ device_printf(self,
+ "Dropped interrupts workaround enabled\n");
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Find companion controllers. According to the spec they always
+ * have lower function numbers so they should be enumerated already.
+ */
+ parent = device_get_parent(self);
+ res = device_get_children(parent, &neighbors, &count);
+ if (res != 0) {
+ device_printf(self, "Error finding companion busses\n");
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+ ncomp = 0;
+ dc = devclass_find("usb");
+ slot = pci_get_slot(self);
+ function = pci_get_function(self);
+ for (i = 0; i < count; i++) {
+ if (pci_get_slot(neighbors[i]) == slot && \
+ pci_get_function(neighbors[i]) < function) {
+ res = device_get_children(neighbors[i],
+ &nbus, &buscount);
+ if (res != 0)
+ continue;
+ if (buscount != 1) {
+ free(nbus, M_TEMP);
+ continue;
+ }
+ if (device_get_devclass(nbus[0]) != dc) {
+ free(nbus, M_TEMP);
+ continue;
+ }
+ bsc = device_get_softc(nbus[0]);
+ free(nbus, M_TEMP);
+ DPRINTF(("ehci_pci_attach: companion %s\n",
+ device_get_nameunit(bsc->bdev)));
+ sc->sc_comps[ncomp++] = bsc;
+ if (ncomp >= EHCI_COMPANION_MAX)
+ break;
+ }
+ }
+ sc->sc_ncomp = ncomp;
+
+ /* Allocate a parent dma tag for DMA maps */
+ err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ NULL, NULL, &sc->sc_bus.parent_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate parent DMA tag (%d)\n",
+ err);
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+
+ /* Allocate a dma tag for transfer buffers */
+ err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate buffer DMA tag (%d)\n",
+ err);
+ ehci_pci_detach(self);
+ return ENXIO;
+ }
+
+ ehci_pci_takecontroller(self);
+ err = ehci_init(sc);
+ if (!err) {
+ sc->sc_flags |= EHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ ehci_pci_detach(self);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ehci_pci_detach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
+ ehci_detach(sc, 0);
+ sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+ }
+
+ /*
+ * disable interrupts that might have been switched on in ehci_init
+ */
+ if (sc->iot && sc->ioh)
+ bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0);
+ if (sc->sc_bus.parent_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.parent_dmatag);
+ if (sc->sc_bus.buffer_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag);
+
+ if (sc->irq_res && sc->ih) {
+ int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ if (sc->sc_bus.bdev) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (sc->io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res);
+ sc->io_res = NULL;
+ sc->iot = 0;
+ sc->ioh = 0;
+ }
+ return 0;
+}
+
+static void
+ehci_pci_takecontroller(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ u_int32_t cparams, eec;
+ uint8_t bios_sem;
+ int eecp, i;
+
+ cparams = EREAD4(sc, EHCI_HCCPARAMS);
+
+ /* Synchronise with the BIOS if it owns the controller. */
+ for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+ eecp = EHCI_EECP_NEXT(eec)) {
+ eec = pci_read_config(self, eecp, 4);
+ if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
+ continue;
+ bios_sem = pci_read_config(self, eecp + EHCI_LEGSUP_BIOS_SEM,
+ 1);
+ if (bios_sem) {
+ pci_write_config(self, eecp + EHCI_LEGSUP_OS_SEM, 1, 1);
+ printf("%s: waiting for BIOS to give up control\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ for (i = 0; i < 5000; i++) {
+ bios_sem = pci_read_config(self, eecp +
+ EHCI_LEGSUP_BIOS_SEM, 1);
+ if (bios_sem == 0)
+ break;
+ DELAY(1000);
+ }
+ if (bios_sem)
+ printf("%s: timed out waiting for BIOS\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ }
+ }
+}
+
+static void
+ehci_pci_givecontroller(device_t self)
+{
+#if 0
+ ehci_softc_t *sc = device_get_softc(self);
+ u_int32_t cparams, eec;
+ int eecp;
+
+ cparams = EREAD4(sc, EHCI_HCCPARAMS);
+ for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+ eecp = EHCI_EECP_NEXT(eec)) {
+ eec = pci_read_config(self, eecp, 4);
+ if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
+ continue;
+ pci_write_config(self, eecp + EHCI_LEGSUP_OS_SEM, 0, 1);
+ }
+#endif
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_pci_probe),
+ DEVMETHOD(device_attach, ehci_pci_attach),
+ DEVMETHOD(device_detach, ehci_pci_detach),
+ DEVMETHOD(device_suspend, ehci_pci_suspend),
+ DEVMETHOD(device_resume, ehci_pci_resume),
+ DEVMETHOD(device_shutdown, ehci_pci_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(ehci_softc_t),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0);
+DRIVER_MODULE(ehci, cardbus, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/ehcireg.h b/sys/legacy/dev/usb/ehcireg.h
new file mode 100644
index 0000000..3c0f5e7
--- /dev/null
+++ b/sys/legacy/dev/usb/ehcireg.h
@@ -0,0 +1,344 @@
+/* $NetBSD: ehcireg.h,v 1.18 2004/10/22 10:38:17 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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.
+ */
+
+/*
+ * The EHCI 0.96 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r096.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/data/usb_20.zip
+ */
+
+#ifndef _DEV_PCI_EHCIREG_H_
+#define _DEV_PCI_EHCIREG_H_
+
+/*** PCI config registers ***/
+
+#define PCI_CBMEM 0x10 /* configuration base MEM */
+
+#define PCI_INTERFACE_EHCI 0x20
+
+#define PCI_USBREV 0x60 /* RO USB protocol revision */
+#define PCI_USBREV_MASK 0xff
+#define PCI_USBREV_PRE_1_0 0x00
+#define PCI_USBREV_1_0 0x10
+#define PCI_USBREV_1_1 0x11
+#define PCI_USBREV_2_0 0x20
+
+#define PCI_EHCI_FLADJ 0x61 /*RW Frame len adj, SOF=59488+6*fladj */
+
+#define PCI_EHCI_PORTWAKECAP 0x62 /* RW Port wake caps (opt) */
+
+/* EHCI Extended Capabilities */
+#define EHCI_EC_LEGSUP 0x01
+
+#define EHCI_EECP_NEXT(x) (((x) >> 8) & 0xff)
+#define EHCI_EECP_ID(x) ((x) & 0xff)
+
+/* Legacy support extended capability */
+#define EHCI_LEGSUP_OS_SEM 0x03 /* OS owned semaphore */
+#define EHCI_LEGSUP_BIOS_SEM 0x02 /* BIOS owned semaphore */
+#define EHCI_LEGSUP_USBLEGCTLSTS 0x04
+
+/*** EHCI capability registers ***/
+
+#define EHCI_CAPLENGTH 0x00 /*RO Capability register length field */
+/* reserved 0x01 */
+#define EHCI_HCIVERSION 0x02 /* RO Interface version number */
+
+#define EHCI_HCSPARAMS 0x04 /* RO Structural parameters */
+#define EHCI_HCS_DEBUGPORT(x) (((x) >> 20) & 0xf)
+#define EHCI_HCS_P_INDICATOR(x) ((x) & 0x10000)
+#define EHCI_HCS_N_CC(x) (((x) >> 12) & 0xf) /* # of companion ctlrs */
+#define EHCI_HCS_N_PCC(x) (((x) >> 8) & 0xf) /* # of ports per comp. */
+#define EHCI_HCS_PPC(x) ((x) & 0x10) /* port power control */
+#define EHCI_HCS_N_PORTS(x) ((x) & 0xf) /* # of ports */
+
+#define EHCI_HCCPARAMS 0x08 /* RO Capability parameters */
+#define EHCI_HCC_EECP(x) (((x) >> 8) & 0xff) /* extended ports caps */
+#define EHCI_HCC_IST(x) (((x) >> 4) & 0xf) /* isoc sched threshold */
+#define EHCI_HCC_ASPC(x) ((x) & 0x4) /* async sched park cap */
+#define EHCI_HCC_PFLF(x) ((x) & 0x2) /* prog frame list flag */
+#define EHCI_HCC_64BIT(x) ((x) & 0x1) /* 64 bit address cap */
+
+#define EHCI_HCSP_PORTROUTE 0x0c /*RO Companion port route description */
+
+/* EHCI operational registers. Offset given by EHCI_CAPLENGTH register */
+#define EHCI_USBCMD 0x00 /* RO, RW, WO Command register */
+#define EHCI_CMD_ITC_M 0x00ff0000 /* RW interrupt threshold ctrl */
+#define EHCI_CMD_ITC_1 0x00010000
+#define EHCI_CMD_ITC_2 0x00020000
+#define EHCI_CMD_ITC_4 0x00040000
+#define EHCI_CMD_ITC_8 0x00080000
+#define EHCI_CMD_ITC_16 0x00100000
+#define EHCI_CMD_ITC_32 0x00200000
+#define EHCI_CMD_ITC_64 0x00400000
+#define EHCI_CMD_ASPME 0x00000800 /* RW/RO async park enable */
+#define EHCI_CMD_ASPMC 0x00000300 /* RW/RO async park count */
+#define EHCI_CMD_LHCR 0x00000080 /* RW light host ctrl reset */
+#define EHCI_CMD_IAAD 0x00000040 /* RW intr on async adv door bell */
+#define EHCI_CMD_ASE 0x00000020 /* RW async sched enable */
+#define EHCI_CMD_PSE 0x00000010 /* RW periodic sched enable */
+#define EHCI_CMD_FLS_M 0x0000000c /* RW/RO frame list size */
+#define EHCI_CMD_FLS(x) (((x) >> 2) & 3) /* RW/RO frame list size */
+#define EHCI_CMD_HCRESET 0x00000002 /* RW reset */
+#define EHCI_CMD_RS 0x00000001 /* RW run/stop */
+
+#define EHCI_USBSTS 0x04 /* RO, RW, RWC Status register */
+#define EHCI_STS_ASS 0x00008000 /* RO async sched status */
+#define EHCI_STS_PSS 0x00004000 /* RO periodic sched status */
+#define EHCI_STS_REC 0x00002000 /* RO reclamation */
+#define EHCI_STS_HCH 0x00001000 /* RO host controller halted */
+#define EHCI_STS_IAA 0x00000020 /* RWC interrupt on async adv */
+#define EHCI_STS_HSE 0x00000010 /* RWC host system error */
+#define EHCI_STS_FLR 0x00000008 /* RWC frame list rollover */
+#define EHCI_STS_PCD 0x00000004 /* RWC port change detect */
+#define EHCI_STS_ERRINT 0x00000002 /* RWC error interrupt */
+#define EHCI_STS_INT 0x00000001 /* RWC interrupt */
+#define EHCI_STS_INTRS(x) ((x) & 0x3f)
+
+#define EHCI_NORMAL_INTRS (EHCI_STS_IAA | EHCI_STS_HSE | EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT)
+
+#define EHCI_USBINTR 0x08 /* RW Interrupt register */
+#define EHCI_INTR_IAAE 0x00000020 /* interrupt on async advance ena */
+#define EHCI_INTR_HSEE 0x00000010 /* host system error ena */
+#define EHCI_INTR_FLRE 0x00000008 /* frame list rollover ena */
+#define EHCI_INTR_PCIE 0x00000004 /* port change ena */
+#define EHCI_INTR_UEIE 0x00000002 /* USB error intr ena */
+#define EHCI_INTR_UIE 0x00000001 /* USB intr ena */
+
+#define EHCI_FRINDEX 0x0c /* RW Frame Index register */
+
+#define EHCI_CTRLDSSEGMENT 0x10 /* RW Control Data Structure Segment */
+
+#define EHCI_PERIODICLISTBASE 0x14 /* RW Periodic List Base */
+#define EHCI_ASYNCLISTADDR 0x18 /* RW Async List Base */
+
+#define EHCI_CONFIGFLAG 0x40 /* RW Configure Flag register */
+#define EHCI_CONF_CF 0x00000001 /* RW configure flag */
+
+#define EHCI_PORTSC(n) (0x40+4*(n)) /* RO, RW, RWC Port Status reg */
+#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current ena */
+#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect ena */
+#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect ena */
+#define EHCI_PS_PTC 0x000f0000 /* RW port test control */
+#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */
+#define EHCI_PS_PO 0x00002000 /* RW port owner */
+#define EHCI_PS_PP 0x00001000 /* RW,RO port power */
+#define EHCI_PS_LS 0x00000c00 /* RO line status */
+#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400)
+#define EHCI_PS_PR 0x00000100 /* RW port reset */
+#define EHCI_PS_SUSP 0x00000080 /* RW suspend */
+#define EHCI_PS_FPR 0x00000040 /* RW force port resume */
+#define EHCI_PS_OCC 0x00000020 /* RWC over current change */
+#define EHCI_PS_OCA 0x00000010 /* RO over current active */
+#define EHCI_PS_PEC 0x00000008 /* RWC port enable change */
+#define EHCI_PS_PE 0x00000004 /* RW port enable */
+#define EHCI_PS_CSC 0x00000002 /* RWC connect status change */
+#define EHCI_PS_CS 0x00000001 /* RO connect status */
+#define EHCI_PS_CLEAR (EHCI_PS_OCC|EHCI_PS_PEC|EHCI_PS_CSC)
+
+#define EHCI_USBMODE 0x68 /* RW USB Device mode register */
+#define EHCI_UM_CM 0x00000003 /* R/WO Controller Mode */
+#define EHCI_UM_CM_IDLE 0x0 /* Idle */
+#define EHCI_UM_CM_HOST 0x3 /* Host Controller */
+#define EHCI_UM_ES 0x00000004 /* R/WO Endian Select */
+#define EHCI_UM_ES_LE 0x0 /* Little-endian byte alignment */
+#define EHCI_UM_ES_BE 0x4 /* Big-endian byte alignment */
+#define EHCI_UM_SDIS 0x00000010 /* R/WO Stream Disable Mode */
+
+#define EHCI_PORT_RESET_COMPLETE 2 /* ms */
+
+#define EHCI_FLALIGN_ALIGN 0x1000
+
+/* No data structure may cross a page boundary. */
+#define EHCI_PAGE_SIZE 0x1000
+#define EHCI_PAGE(x) ((x) &~ 0xfff)
+#define EHCI_PAGE_OFFSET(x) ((x) & 0xfff)
+#if defined(__FreeBSD__)
+#define EHCI_PAGE_MASK(x) ((x) & 0xfff)
+#endif
+
+typedef u_int32_t ehci_link_t;
+#define EHCI_LINK_TERMINATE 0x00000001
+#define EHCI_LINK_TYPE(x) ((x) & 0x00000006)
+#define EHCI_LINK_ITD 0x0
+#define EHCI_LINK_QH 0x2
+#define EHCI_LINK_SITD 0x4
+#define EHCI_LINK_FSTN 0x6
+#define EHCI_LINK_ADDR(x) ((x) &~ 0x1f)
+
+typedef u_int32_t ehci_physaddr_t;
+
+typedef u_int32_t ehci_isoc_trans_t;
+typedef u_int32_t ehci_isoc_bufr_ptr_t;
+
+/* Isochronous Transfer Descriptor */
+typedef struct {
+ ehci_link_t itd_next;
+ ehci_isoc_trans_t itd_ctl[8];
+#define EHCI_ITD_GET_STATUS(x) (((x) >> 28) & 0xf)
+#define EHCI_ITD_SET_STATUS(x) (((x) & 0xf) << 28)
+#define EHCI_ITD_ACTIVE 0x80000000
+#define EHCI_ITD_BUF_ERR 0x40000000
+#define EHCI_ITD_BABBLE 0x20000000
+#define EHCI_ITD_ERROR 0x10000000
+#define EHCI_ITD_GET_LEN(x) (((x) >> 16) & 0xfff)
+#define EHCI_ITD_SET_LEN(x) (((x) & 0xfff) << 16)
+#define EHCI_ITD_IOC 0x8000
+#define EHCI_ITD_GET_IOC(x) (((x) >> 15) & 1)
+#define EHCI_ITD_SET_IOC(x) (((x) << 15) & EHCI_ITD_IOC)
+#define EHCI_ITD_GET_PG(x) (((x) >> 12) & 0xf)
+#define EHCI_ITD_SET_PG(x) (((x) & 0xf) << 12)
+#define EHCI_ITD_GET_OFFS(x) (((x) >> 0) & 0xfff)
+#define EHCI_ITD_SET_OFFS(x) (((x) & 0xfff) << 0)
+ ehci_isoc_bufr_ptr_t itd_bufr[7];
+#define EHCI_ITD_GET_BPTR(x) ((x) & 0xfffff000)
+#define EHCI_ITD_SET_BPTR(x) ((x) & 0xfffff000)
+#define EHCI_ITD_GET_EP(x) (((x) >> 8) & 0xf)
+#define EHCI_ITD_SET_EP(x) (((x) & 0xf) << 8)
+#define EHCI_ITD_GET_DADDR(x) ((x) & 0x7f)
+#define EHCI_ITD_SET_DADDR(x) ((x) & 0x7f)
+#define EHCI_ITD_GET_DIR(x) (((x) >> 11) & 1)
+#define EHCI_ITD_SET_DIR(x) (((x) & 1) << 11)
+#define EHCI_ITD_GET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_SET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_GET_MULTI(x) ((x) & 0x3)
+#define EHCI_ITD_SET_MULTI(x) ((x) & 0x3)
+} ehci_itd_t;
+#define EHCI_ITD_ALIGN 32
+
+/* Split Transaction Isochronous Transfer Descriptor */
+typedef struct {
+ ehci_link_t sitd_next;
+ /* XXX many more */
+} ehci_sitd_t;
+#define EHCI_SITD_ALIGN 32
+
+/* Queue Element Transfer Descriptor */
+#define EHCI_QTD_NBUFFERS 5
+typedef struct {
+ ehci_link_t qtd_next;
+ ehci_link_t qtd_altnext;
+ u_int32_t qtd_status;
+#define EHCI_QTD_GET_STATUS(x) (((x) >> 0) & 0xff)
+#define EHCI_QTD_SET_STATUS(x) ((x) << 0)
+#define EHCI_QTD_ACTIVE 0x80
+#define EHCI_QTD_HALTED 0x40
+#define EHCI_QTD_BUFERR 0x20
+#define EHCI_QTD_BABBLE 0x10
+#define EHCI_QTD_XACTERR 0x08
+#define EHCI_QTD_MISSEDMICRO 0x04
+#define EHCI_QTD_SPLITXSTATE 0x02
+#define EHCI_QTD_PINGSTATE 0x01
+#define EHCI_QTD_STATERRS 0x7c
+#define EHCI_QTD_GET_PID(x) (((x) >> 8) & 0x3)
+#define EHCI_QTD_SET_PID(x) ((x) << 8)
+#define EHCI_QTD_PID_OUT 0x0
+#define EHCI_QTD_PID_IN 0x1
+#define EHCI_QTD_PID_SETUP 0x2
+#define EHCI_QTD_GET_CERR(x) (((x) >> 10) & 0x3)
+#define EHCI_QTD_SET_CERR(x) ((x) << 10)
+#define EHCI_QTD_GET_C_PAGE(x) (((x) >> 12) & 0x7)
+#define EHCI_QTD_SET_C_PAGE(x) ((x) << 12)
+#define EHCI_QTD_GET_IOC(x) (((x) >> 15) & 0x1)
+#define EHCI_QTD_IOC 0x00008000
+#define EHCI_QTD_GET_BYTES(x) (((x) >> 16) & 0x7fff)
+#define EHCI_QTD_SET_BYTES(x) ((x) << 16)
+#define EHCI_QTD_GET_TOGGLE(x) (((x) >> 31) & 0x1)
+#define EHCI_QTD_SET_TOGGLE(x) ((x) << 31)
+#define EHCI_QTD_TOGGLE_MASK 0x80000000
+ ehci_physaddr_t qtd_buffer[EHCI_QTD_NBUFFERS];
+ ehci_physaddr_t qtd_buffer_hi[EHCI_QTD_NBUFFERS];
+} ehci_qtd_t;
+#define EHCI_QTD_ALIGN 32
+
+#define EHCI_QTD_STATUS_BITS \
+ "\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE\4XACTERR\3MISSED\2SPLIT\1PING"
+
+/* Queue Head */
+typedef struct {
+ ehci_link_t qh_link;
+ u_int32_t qh_endp;
+#define EHCI_QH_GET_ADDR(x) (((x) >> 0) & 0x7f) /* endpoint addr */
+#define EHCI_QH_SET_ADDR(x) (x)
+#define EHCI_QH_ADDRMASK 0x0000007f
+#define EHCI_QH_GET_INACT(x) (((x) >> 7) & 0x01) /* inactivate on next */
+#define EHCI_QH_INACT 0x00000080
+#define EHCI_QH_GET_ENDPT(x) (((x) >> 8) & 0x0f) /* endpoint no */
+#define EHCI_QH_SET_ENDPT(x) ((x) << 8)
+#define EHCI_QH_GET_EPS(x) (((x) >> 12) & 0x03) /* endpoint speed */
+#define EHCI_QH_SET_EPS(x) ((x) << 12)
+#define EHCI_QH_SPEED_FULL 0x0
+#define EHCI_QH_SPEED_LOW 0x1
+#define EHCI_QH_SPEED_HIGH 0x2
+#define EHCI_QH_GET_DTC(x) (((x) >> 14) & 0x01) /* data toggle control */
+#define EHCI_QH_DTC 0x00004000
+#define EHCI_QH_GET_HRECL(x) (((x) >> 15) & 0x01) /* head of reclamation */
+#define EHCI_QH_HRECL 0x00008000
+#define EHCI_QH_GET_MPL(x) (((x) >> 16) & 0x7ff) /* max packet len */
+#define EHCI_QH_SET_MPL(x) ((x) << 16)
+#define EHCI_QH_MPLMASK 0x07ff0000
+#define EHCI_QH_GET_CTL(x) (((x) >> 27) & 0x01) /* control endpoint */
+#define EHCI_QH_CTL 0x08000000
+#define EHCI_QH_GET_NRL(x) (((x) >> 28) & 0x0f) /* NAK reload */
+#define EHCI_QH_SET_NRL(x) ((x) << 28)
+ u_int32_t qh_endphub;
+#define EHCI_QH_GET_SMASK(x) (((x) >> 0) & 0xff) /* intr sched mask */
+#define EHCI_QH_SET_SMASK(x) ((x) << 0)
+#define EHCI_QH_GET_CMASK(x) (((x) >> 8) & 0xff) /* split completion mask */
+#define EHCI_QH_SET_CMASK(x) ((x) << 8)
+#define EHCI_QH_GET_HUBA(x) (((x) >> 16) & 0x7f) /* hub address */
+#define EHCI_QH_SET_HUBA(x) ((x) << 16)
+#define EHCI_QH_GET_PORT(x) (((x) >> 23) & 0x7f) /* hub port */
+#define EHCI_QH_SET_PORT(x) ((x) << 23)
+#define EHCI_QH_GET_MULT(x) (((x) >> 30) & 0x03) /* pipe multiplier */
+#define EHCI_QH_SET_MULT(x) ((x) << 30)
+ ehci_link_t qh_curqtd;
+ ehci_qtd_t qh_qtd;
+} ehci_qh_t;
+#define EHCI_QH_ALIGN 32
+
+/* Periodic Frame Span Traversal Node */
+typedef struct {
+ ehci_link_t fstn_link;
+ ehci_link_t fstn_back;
+} ehci_fstn_t;
+#define EHCI_FSTN_ALIGN 32
+
+#endif /* _DEV_PCI_EHCIREG_H_ */
diff --git a/sys/legacy/dev/usb/ehcivar.h b/sys/legacy/dev/usb/ehcivar.h
new file mode 100644
index 0000000..fdd19ba
--- /dev/null
+++ b/sys/legacy/dev/usb/ehcivar.h
@@ -0,0 +1,278 @@
+/* $NetBSD: ehcivar.h,v 1.19 2005/04/29 15:04:29 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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.
+ */
+
+typedef struct ehci_soft_qtd {
+ ehci_qtd_t qtd;
+ struct ehci_soft_qtd *nextqtd; /* mirrors nextqtd in TD */
+ ehci_physaddr_t physaddr;
+ usbd_xfer_handle xfer;
+ LIST_ENTRY(ehci_soft_qtd) hnext;
+ u_int16_t len;
+} ehci_soft_qtd_t;
+#define EHCI_SQTD_SIZE ((sizeof (struct ehci_soft_qtd) + EHCI_QTD_ALIGN - 1) / EHCI_QTD_ALIGN * EHCI_QTD_ALIGN)
+#define EHCI_SQTD_CHUNK (EHCI_PAGE_SIZE / EHCI_SQTD_SIZE)
+
+typedef struct ehci_soft_qh {
+ ehci_qh_t qh;
+ struct ehci_soft_qh *next;
+ struct ehci_soft_qh *prev;
+ struct ehci_soft_qtd *sqtd;
+ struct ehci_soft_qtd *inactivesqtd;
+ ehci_physaddr_t physaddr;
+ int islot; /* Interrupt list slot. */
+} ehci_soft_qh_t;
+#define EHCI_SQH_SIZE ((sizeof (struct ehci_soft_qh) + EHCI_QH_ALIGN - 1) / EHCI_QH_ALIGN * EHCI_QH_ALIGN)
+#define EHCI_SQH_CHUNK (EHCI_PAGE_SIZE / EHCI_SQH_SIZE)
+
+typedef struct ehci_soft_itd {
+ ehci_itd_t itd;
+ union {
+ struct {
+ /* soft_itds links in a periodic frame*/
+ struct ehci_soft_itd *next;
+ struct ehci_soft_itd *prev;
+ } frame_list;
+ /* circular list of free itds */
+ LIST_ENTRY(ehci_soft_itd) free_list;
+ } u;
+ struct ehci_soft_itd *xfer_next; /* Next soft_itd in xfer */
+ ehci_physaddr_t physaddr;
+ usb_dma_t dma;
+ int offs;
+ int slot;
+ struct timeval t; /* store free time */
+} ehci_soft_itd_t;
+#define EHCI_ITD_SIZE ((sizeof(struct ehci_soft_itd) + EHCI_QH_ALIGN - 1) / EHCI_ITD_ALIGN * EHCI_ITD_ALIGN)
+#define EHCI_ITD_CHUNK (EHCI_PAGE_SIZE / EHCI_ITD_SIZE)
+
+struct ehci_xfer {
+ struct usbd_xfer xfer;
+ struct usb_task abort_task;
+ LIST_ENTRY(ehci_xfer) inext; /* list of active xfers */
+ ehci_soft_qtd_t *sqtdstart;
+ ehci_soft_qtd_t *sqtdend;
+ ehci_soft_itd_t *itdstart;
+ ehci_soft_itd_t *itdend;
+ u_int isoc_len;
+ u_int32_t ehci_xfer_flags;
+#ifdef DIAGNOSTIC
+ int isdone;
+#endif
+};
+#define EHCI_XFER_ABORTING 0x0001 /* xfer is aborting. */
+#define EHCI_XFER_ABORTWAIT 0x0002 /* abort completion is being awaited. */
+
+#define EXFER(xfer) ((struct ehci_xfer *)(xfer))
+
+/*
+ * Information about an entry in the interrupt list.
+ */
+struct ehci_soft_islot {
+ ehci_soft_qh_t *sqh; /* Queue Head. */
+};
+
+#define EHCI_FRAMELIST_MAXCOUNT 1024
+#define EHCI_IPOLLRATES 8 /* Poll rates (1ms, 2, 4, 8 ... 128) */
+#define EHCI_INTRQHS ((1 << EHCI_IPOLLRATES) - 1)
+#define EHCI_MAX_POLLRATE (1 << (EHCI_IPOLLRATES - 1))
+#define EHCI_IQHIDX(lev, pos) \
+ ((((pos) & ((1 << (lev)) - 1)) | (1 << (lev))) - 1)
+#define EHCI_ILEV_IVAL(lev) (1 << (lev))
+
+#define EHCI_HASH_SIZE 128
+#define EHCI_COMPANION_MAX 8
+
+#define EHCI_FREE_LIST_INTERVAL 100
+
+#define EHCI_SCFLG_DONEINIT 0x0001 /* ehci_init() has been called. */
+#define EHCI_SCFLG_LOSTINTRBUG 0x0002 /* workaround for VIA / ATI chipsets */
+#define EHCI_SCFLG_SETMODE 0x0004 /* set bridge mode again after init (Marvell) */
+#define EHCI_SCFLG_FORCESPEED 0x0008 /* force speed (Marvell) */
+#define EHCI_SCFLG_NORESTERM 0x0010 /* don't terminate reset sequence (Marvell) */
+#define EHCI_SCFLG_BIGEDESC 0x0020 /* big-endian byte order descriptors */
+#define EHCI_SCFLG_BIGEMMIO 0x0040 /* big-endian byte order MMIO */
+#define EHCI_SCFLG_TT 0x0080 /* transaction translator present */
+
+typedef struct ehci_softc {
+ struct usbd_bus sc_bus; /* base device */
+ int sc_flags;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t sc_size;
+ void *ih;
+
+ struct resource *io_res;
+ struct resource *irq_res;
+ u_int sc_offs; /* offset to operational regs */
+
+ char sc_vendor[32]; /* vendor string for root hub */
+ int sc_id_vendor; /* vendor ID for root hub */
+
+ u_int32_t sc_cmd; /* shadow of cmd reg during suspend */
+
+ u_int sc_ncomp;
+ u_int sc_npcomp;
+ struct usbd_bus *sc_comps[EHCI_COMPANION_MAX];
+
+ usb_dma_t sc_fldma;
+ ehci_link_t *sc_flist;
+ u_int sc_flsize;
+
+ struct ehci_soft_islot sc_islots[EHCI_INTRQHS];
+
+ /* jcmm - an array matching sc_flist, but with software pointers,
+ * not hardware address pointers
+ */
+ struct ehci_soft_itd **sc_softitds;
+
+ LIST_HEAD(, ehci_xfer) sc_intrhead;
+
+ ehci_soft_qh_t *sc_freeqhs;
+ ehci_soft_qtd_t *sc_freeqtds;
+ LIST_HEAD(sc_freeitds, ehci_soft_itd) sc_freeitds;
+
+ int sc_noport;
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+ usbd_xfer_handle sc_intrxfer;
+ char sc_isreset;
+#ifdef USB_USE_SOFTINTR
+ char sc_softwake;
+#endif /* USB_USE_SOFTINTR */
+
+ u_int32_t sc_eintrs;
+ ehci_soft_qh_t *sc_async_head;
+
+ STAILQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */
+
+ struct lock sc_doorbell_lock;
+
+ struct callout sc_tmo_intrlist;
+
+ char sc_dying;
+} ehci_softc_t;
+
+#define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
+#define EREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (a))
+#define EREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (a))
+#define EWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (a), (x))
+#define EWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (a), (x))
+#define EWRITE4(sc, a, x) bus_space_write_4((sc)->iot, (sc)->ioh, (a), (x))
+#define EOREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a))
+#define EOREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a))
+#define EOREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a))
+#define EOWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x))
+#define EOWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x))
+#define EOWRITE4(sc, a, x) bus_space_write_4((sc)->iot, (sc)->ioh, (sc)->sc_offs+(a), (x))
+
+#ifdef USB_EHCI_BIG_ENDIAN_DESC
+/*
+ * Handle byte order conversion between host and ``host controller''.
+ * Typically the latter is little-endian but some controllers require
+ * big-endian in which case we may need to manually swap.
+ */
+static __inline uint32_t
+htohc32(const struct ehci_softc *sc, const uint32_t v)
+{
+ return sc->sc_flags & EHCI_SCFLG_BIGEDESC ? htobe32(v) : htole32(v);
+}
+
+static __inline uint16_t
+htohc16(const struct ehci_softc *sc, const uint16_t v)
+{
+ return sc->sc_flags & EHCI_SCFLG_BIGEDESC ? htobe16(v) : htole16(v);
+}
+
+static __inline uint32_t
+hc32toh(const struct ehci_softc *sc, const uint32_t v)
+{
+ return sc->sc_flags & EHCI_SCFLG_BIGEDESC ? be32toh(v) : le32toh(v);
+}
+
+static __inline uint16_t
+hc16toh(const struct ehci_softc *sc, const uint16_t v)
+{
+ return sc->sc_flags & EHCI_SCFLG_BIGEDESC ? be16toh(v) : le16toh(v);
+}
+#else
+/*
+ * Normal little-endian only conversion routines.
+ */
+static __inline uint32_t
+htohc32(const struct ehci_softc *sc, const uint32_t v)
+{
+ return htole32(v);
+}
+
+static __inline uint16_t
+htohc16(const struct ehci_softc *sc, const uint16_t v)
+{
+ return htole16(v);
+}
+
+static __inline uint32_t
+hc32toh(const struct ehci_softc *sc, const uint32_t v)
+{
+ return le32toh(v);
+}
+
+static __inline uint16_t
+hc16toh(const struct ehci_softc *sc, const uint16_t v)
+{
+ return le16toh(v);
+}
+#endif
+
+usbd_status ehci_reset(ehci_softc_t *);
+usbd_status ehci_init(ehci_softc_t *);
+int ehci_intr(void *);
+int ehci_detach(ehci_softc_t *, int);
+void ehci_power(int state, void *priv);
+void ehci_shutdown(void *v);
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+void ehci_dump_regs(ehci_softc_t *);
+void ehci_dump_sqtds(ehci_softc_t *, ehci_soft_qtd_t *);
+void ehci_dump_qtd(ehci_softc_t *, ehci_qtd_t *);
+void ehci_dump_sqtd(ehci_softc_t *, ehci_soft_qtd_t *);
+void ehci_dump_sqh(ehci_softc_t *, ehci_soft_qh_t *);
+void ehci_dump_itd(ehci_softc_t *, struct ehci_soft_itd *);
+void ehci_dump_sitd(ehci_softc_t *, struct ehci_soft_itd *);
+void ehci_dump_exfer(struct ehci_xfer *);
diff --git a/sys/legacy/dev/usb/hid.c b/sys/legacy/dev/usb/hid.c
new file mode 100644
index 0000000..70facb6
--- /dev/null
+++ b/sys/legacy/dev/usb/hid.c
@@ -0,0 +1,469 @@
+/* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*-
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/hid.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+static void hid_clear_local(struct hid_item *);
+
+#define MAXUSAGE 100
+struct hid_data {
+ u_char *start;
+ u_char *end;
+ u_char *p;
+ struct hid_item cur;
+ int32_t usages[MAXUSAGE];
+ int nu;
+ int minset;
+ int multi;
+ int multimax;
+ int kindset;
+};
+
+static void
+hid_clear_local(struct hid_item *c)
+{
+
+ c->usage = 0;
+ c->usage_minimum = 0;
+ c->usage_maximum = 0;
+ c->designator_index = 0;
+ c->designator_minimum = 0;
+ c->designator_maximum = 0;
+ c->string_index = 0;
+ c->string_minimum = 0;
+ c->string_maximum = 0;
+ c->set_delimiter = 0;
+}
+
+struct hid_data *
+hid_start_parse(void *d, int len, int kindset)
+{
+ struct hid_data *s;
+
+ s = malloc(sizeof *s, M_TEMP, M_WAITOK|M_ZERO);
+ s->start = s->p = d;
+ s->end = (char *)d + len;
+ s->kindset = kindset;
+ return (s);
+}
+
+void
+hid_end_parse(struct hid_data *s)
+{
+
+ while (s->cur.next != NULL) {
+ struct hid_item *hi = s->cur.next->next;
+ free(s->cur.next, M_TEMP);
+ s->cur.next = hi;
+ }
+ free(s, M_TEMP);
+}
+
+int
+hid_get_item(struct hid_data *s, struct hid_item *h)
+{
+ struct hid_item *c = &s->cur;
+ unsigned int bTag, bType, bSize;
+ u_int32_t oldpos;
+ u_char *data;
+ int32_t dval;
+ u_char *p;
+ struct hid_item *hi;
+ int i;
+
+ top:
+ if (s->multimax != 0) {
+ if (s->multi < s->multimax) {
+ c->usage = s->usages[min(s->multi, s->nu-1)];
+ s->multi++;
+ *h = *c;
+ c->loc.pos += c->loc.size;
+ h->next = 0;
+ return (1);
+ } else {
+ c->loc.count = s->multimax;
+ s->multimax = 0;
+ s->nu = 0;
+ hid_clear_local(c);
+ }
+ }
+ for (;;) {
+ p = s->p;
+ if (p >= s->end)
+ return (0);
+
+ bSize = *p++;
+ if (bSize == 0xfe) {
+ /* long item */
+ bSize = *p++;
+ bSize |= *p++ << 8;
+ bTag = *p++;
+ data = p;
+ p += bSize;
+ bType = 0xff; /* XXX what should it be */
+ } else {
+ /* short item */
+ bTag = bSize >> 4;
+ bType = (bSize >> 2) & 3;
+ bSize &= 3;
+ if (bSize == 3) bSize = 4;
+ data = p;
+ p += bSize;
+ }
+ s->p = p;
+ switch(bSize) {
+ case 0:
+ dval = 0;
+ break;
+ case 1:
+ dval = (int8_t)*data++;
+ break;
+ case 2:
+ dval = *data++;
+ dval |= *data++ << 8;
+ dval = (int16_t)dval;
+ break;
+ case 4:
+ dval = *data++;
+ dval |= *data++ << 8;
+ dval |= *data++ << 16;
+ dval |= *data++ << 24;
+ break;
+ default:
+ printf("BAD LENGTH %d\n", bSize);
+ continue;
+ }
+
+ switch (bType) {
+ case 0: /* Main */
+ switch (bTag) {
+ case 8: /* Input */
+ if (!(s->kindset & (1 << hid_input))) {
+ if (s->nu > 0)
+ s->nu--;
+ continue;
+ }
+ c->kind = hid_input;
+ c->flags = dval;
+ ret:
+ if (c->flags & HIO_VARIABLE) {
+ s->multimax = c->loc.count;
+ s->multi = 0;
+ c->loc.count = 1;
+ if (s->minset) {
+ for (i = c->usage_minimum;
+ i <= c->usage_maximum;
+ i++) {
+ s->usages[s->nu] = i;
+ if (s->nu < MAXUSAGE-1)
+ s->nu++;
+ }
+ s->minset = 0;
+ }
+ goto top;
+ } else {
+ *h = *c;
+ h->next = 0;
+ c->loc.pos +=
+ c->loc.size * c->loc.count;
+ hid_clear_local(c);
+ s->minset = 0;
+ return (1);
+ }
+ case 9: /* Output */
+ if (!(s->kindset & (1 << hid_output))) {
+ if (s->nu > 0)
+ s->nu--;
+ continue;
+ }
+ c->kind = hid_output;
+ c->flags = dval;
+ goto ret;
+ case 10: /* Collection */
+ c->kind = hid_collection;
+ c->collection = dval;
+ c->collevel++;
+ *h = *c;
+ hid_clear_local(c);
+ s->nu = 0;
+ return (1);
+ case 11: /* Feature */
+ if (!(s->kindset & (1 << hid_feature))) {
+ if (s->nu > 0)
+ s->nu--;
+ continue;
+ }
+ c->kind = hid_feature;
+ c->flags = dval;
+ goto ret;
+ case 12: /* End collection */
+ c->kind = hid_endcollection;
+ c->collevel--;
+ *h = *c;
+ hid_clear_local(c);
+ s->nu = 0;
+ return (1);
+ default:
+ printf("Main bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ case 1: /* Global */
+ switch (bTag) {
+ case 0:
+ c->_usage_page = dval << 16;
+ break;
+ case 1:
+ c->logical_minimum = dval;
+ break;
+ case 2:
+ c->logical_maximum = dval;
+ break;
+ case 3:
+ c->physical_minimum = dval;
+ break;
+ case 4:
+ c->physical_maximum = dval;
+ break;
+ case 5:
+ c->unit_exponent = dval;
+ break;
+ case 6:
+ c->unit = dval;
+ break;
+ case 7:
+ c->loc.size = dval;
+ break;
+ case 8:
+ c->report_ID = dval;
+ break;
+ case 9:
+ c->loc.count = dval;
+ break;
+ case 10: /* Push */
+ hi = malloc(sizeof *hi, M_TEMP, M_WAITOK);
+ *hi = s->cur;
+ c->next = hi;
+ break;
+ case 11: /* Pop */
+ hi = c->next;
+ oldpos = c->loc.pos;
+ s->cur = *hi;
+ c->loc.pos = oldpos;
+ free(hi, M_TEMP);
+ break;
+ default:
+ printf("Global bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ case 2: /* Local */
+ switch (bTag) {
+ case 0:
+ if (bSize == 1)
+ dval = c->_usage_page | (dval&0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval&0xffff);
+ c->usage = dval;
+ if (s->nu < MAXUSAGE)
+ s->usages[s->nu++] = dval;
+ /* else XXX */
+ break;
+ case 1:
+ s->minset = 1;
+ if (bSize == 1)
+ dval = c->_usage_page | (dval&0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval&0xffff);
+ c->usage_minimum = dval;
+ break;
+ case 2:
+ if (bSize == 1)
+ dval = c->_usage_page | (dval&0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval&0xffff);
+ c->usage_maximum = dval;
+ break;
+ case 3:
+ c->designator_index = dval;
+ break;
+ case 4:
+ c->designator_minimum = dval;
+ break;
+ case 5:
+ c->designator_maximum = dval;
+ break;
+ case 7:
+ c->string_index = dval;
+ break;
+ case 8:
+ c->string_minimum = dval;
+ break;
+ case 9:
+ c->string_maximum = dval;
+ break;
+ case 10:
+ c->set_delimiter = dval;
+ break;
+ default:
+ printf("Local bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ default:
+ printf("default bType=%d\n", bType);
+ break;
+ }
+ }
+}
+
+int
+hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *idp)
+{
+ struct hid_data *d;
+ struct hid_item h;
+ int hi, lo, size, id;
+
+ id = 0;
+ hi = lo = -1;
+ for (d = hid_start_parse(buf, len, 1<<k); hid_get_item(d, &h); )
+ if (h.kind == k) {
+ if (h.report_ID != 0 && !id)
+ id = h.report_ID;
+ if (h.report_ID == id) {
+ if (lo < 0)
+ lo = h.loc.pos;
+ hi = h.loc.pos + h.loc.size * h.loc.count;
+ }
+ }
+ hid_end_parse(d);
+ size = hi - lo;
+ if (id != 0) {
+ size += 8;
+ *idp = id; /* XXX wrong */
+ } else
+ *idp = 0;
+ return ((size + 7) / 8);
+}
+
+int
+hid_locate(void *desc, int size, u_int32_t u, enum hid_kind k,
+ struct hid_location *loc, u_int32_t *flags)
+{
+ struct hid_data *d;
+ struct hid_item h;
+
+ for (d = hid_start_parse(desc, size, 1<<k); hid_get_item(d, &h); ) {
+ if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
+ if (loc != NULL)
+ *loc = h.loc;
+ if (flags != NULL)
+ *flags = h.flags;
+ hid_end_parse(d);
+ return (1);
+ }
+ }
+ hid_end_parse(d);
+ loc->size = 0;
+ return (0);
+}
+
+u_long
+hid_get_data(u_char *buf, struct hid_location *loc)
+{
+ u_int hpos = loc->pos;
+ u_int hsize = loc->size;
+ u_int32_t data;
+ int i, s;
+
+ DPRINTFN(10, ("hid_get_data: loc %d/%d\n", hpos, hsize));
+
+ if (hsize == 0)
+ return (0);
+
+ data = 0;
+ s = hpos / 8;
+ for (i = hpos; i < hpos+hsize; i += 8)
+ data |= buf[i / 8] << ((i / 8 - s) * 8);
+ data >>= hpos % 8;
+ data &= (1 << hsize) - 1;
+ hsize = 32 - hsize;
+ /* Sign extend */
+ data = ((int32_t)data << hsize) >> hsize;
+ DPRINTFN(10,("hid_get_data: loc %d/%d = %lu\n",
+ loc->pos, loc->size, (long)data));
+ return (data);
+}
+
+int
+hid_is_collection(void *desc, int size, u_int32_t usage)
+{
+ struct hid_data *hd;
+ struct hid_item hi;
+ int err;
+
+ hd = hid_start_parse(desc, size, hid_input);
+ if (hd == NULL)
+ return (0);
+
+ err = hid_get_item(hd, &hi) &&
+ hi.kind == hid_collection &&
+ hi.usage == usage;
+ hid_end_parse(hd);
+ return (err);
+}
diff --git a/sys/legacy/dev/usb/hid.h b/sys/legacy/dev/usb/hid.h
new file mode 100644
index 0000000..a4ab7d2
--- /dev/null
+++ b/sys/legacy/dev/usb/hid.h
@@ -0,0 +1,91 @@
+/* $NetBSD: hid.h,v 1.6 2000/06/01 14:28:57 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+enum hid_kind {
+ hid_input, hid_output, hid_feature, hid_collection, hid_endcollection
+};
+
+struct hid_location {
+ u_int32_t size;
+ u_int32_t count;
+ u_int32_t pos;
+};
+
+struct hid_item {
+ /* Global */
+ int32_t _usage_page;
+ int32_t logical_minimum;
+ int32_t logical_maximum;
+ int32_t physical_minimum;
+ int32_t physical_maximum;
+ int32_t unit_exponent;
+ int32_t unit;
+ int32_t report_ID;
+ /* Local */
+ int32_t usage;
+ int32_t usage_minimum;
+ int32_t usage_maximum;
+ int32_t designator_index;
+ int32_t designator_minimum;
+ int32_t designator_maximum;
+ int32_t string_index;
+ int32_t string_minimum;
+ int32_t string_maximum;
+ int32_t set_delimiter;
+ /* Misc */
+ int32_t collection;
+ int collevel;
+ enum hid_kind kind;
+ u_int32_t flags;
+ /* Location */
+ struct hid_location loc;
+ /* */
+ struct hid_item *next;
+};
+
+struct hid_data *hid_start_parse(void *d, int len, int kindset);
+void hid_end_parse(struct hid_data *s);
+int hid_get_item(struct hid_data *s, struct hid_item *h);
+int hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *id);
+int hid_locate(void *desc, int size, u_int32_t usage,
+ enum hid_kind kind, struct hid_location *loc,
+ u_int32_t *flags);
+u_long hid_get_data(u_char *buf, struct hid_location *loc);
+int hid_is_collection(void *desc, int size, u_int32_t usage);
diff --git a/sys/legacy/dev/usb/if_aue.c b/sys/legacy/dev/usb/if_aue.c
new file mode 100644
index 0000000..1c6d8a3
--- /dev/null
+++ b/sys/legacy/dev/usb/if_aue.c
@@ -0,0 +1,1498 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Copyright (c) 2006
+ * Alfred Perlstein <alfred@freebsd.org>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver.
+ * Datasheet is available from http://www.admtek.com.tw.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ *
+ * SMP locking by Alfred Perlstein <alfred@freebsd.org>.
+ * RED Inc.
+ */
+
+/*
+ * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet
+ * support: the control endpoint for reading/writing registers, burst
+ * read endpoint for packet reception, burst write for packet transmission
+ * and one for "interrupts." The chip uses the same RX filter scheme
+ * as the other ADMtek ethernet parts: one perfect filter entry for the
+ * the station address and a 64-bit multicast hash table. The chip supports
+ * both MII and HomePNA attachments.
+ *
+ * Since the maximum data transfer speed of USB is supposed to be 12Mbps,
+ * you're never really going to get 100Mbps speeds from this device. I
+ * think the idea is to allow the device to connect to 10 or 100Mbps
+ * networks, not necessarily to provide 100Mbps performance. Also, since
+ * the controller uses an external PHY chip, it's possible that board
+ * designers might simply choose a 10Mbps PHY.
+ *
+ * Registers are accessed using usbd_do_request(). Packet transfers are
+ * done using usbd_transfer() and friends.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/kdb.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sx.h>
+#include <sys/taskqueue.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/if_auereg.h>
+
+MODULE_DEPEND(aue, usb, 1, 1, 1);
+MODULE_DEPEND(aue, ether, 1, 1, 1);
+MODULE_DEPEND(aue, miibus, 1, 1, 1);
+
+/* "device miibus" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+/*
+ * Various supported device vendors/products.
+ */
+struct aue_type {
+ struct usb_devno aue_dev;
+ u_int16_t aue_flags;
+#define LSYS 0x0001 /* use Linksys reset */
+#define PNA 0x0002 /* has Home PNA */
+#define PII 0x0004 /* Pegasus II chip */
+};
+
+static const struct aue_type aue_devs[] = {
+ {{ USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1}, PNA|PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000}, LSYS },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4}, PNA },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5}, PNA },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8}, PII },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9}, PNA },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10}, 0 },
+ {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA}, 0 },
+ {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC}, 0 },
+ {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001}, PII },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS}, PNA },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII}, PII },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2}, PII },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3}, PII },
+ {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_4}, PII },
+ {{ USB_VENDOR_AEI, USB_PRODUCT_AEI_FASTETHERNET}, PII },
+ {{ USB_VENDOR_ALLIEDTELESYN, USB_PRODUCT_ALLIEDTELESYN_ATUSB100}, PII },
+ {{ USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC110T}, PII },
+ {{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN}, PII },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100}, 0 },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100}, PNA },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100}, 0 },
+ {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100}, PII },
+ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX}, 0 },
+ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS},PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4}, LSYS|PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1}, LSYS },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX}, LSYS },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA}, PNA },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3}, LSYS|PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2}, LSYS|PII },
+ {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650}, LSYS },
+ {{ USB_VENDOR_ELCON, USB_PRODUCT_ELCON_PLAN}, PNA|PII },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSB20}, PII },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0}, 0 },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1}, LSYS },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2}, 0 },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3}, LSYS },
+ {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX}, PII },
+ {{ USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET}, 0 },
+ {{ USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNBR402W}, 0 },
+ {{ USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100}, PII },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_HN210E}, PII },
+ {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX}, 0 },
+ {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS}, PII },
+ {{ USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX}, 0 },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1}, LSYS|PII },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T}, LSYS },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX}, LSYS },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1}, LSYS|PNA },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA}, LSYS },
+ {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2}, LSYS|PII },
+ {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1}, 0 },
+ {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5}, 0 },
+ {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5}, PII },
+ {{ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110}, PII },
+ {{ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101}, PII },
+ {{ USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM}, PII },
+ {{ USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_USBTOETHER}, PII },
+ {{ USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC},PII },
+ {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB}, 0 },
+ {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB}, PII },
+ {{ USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100}, 0 },
+ {{ USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB110}, PII },
+};
+#define aue_lookup(v, p) ((const struct aue_type *)usb_lookup(aue_devs, v, p))
+
+static device_probe_t aue_match;
+static device_attach_t aue_attach;
+static device_detach_t aue_detach;
+static device_shutdown_t aue_shutdown;
+static miibus_readreg_t aue_miibus_readreg;
+static miibus_writereg_t aue_miibus_writereg;
+static miibus_statchg_t aue_miibus_statchg;
+
+static void aue_reset_pegasus_II(struct aue_softc *sc);
+static int aue_encap(struct aue_softc *, struct mbuf *, int);
+#ifdef AUE_INTR_PIPE
+static void aue_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#endif
+static void aue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void aue_rxeof_thread(struct aue_softc *sc);
+static void aue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void aue_txeof_thread(struct aue_softc *);
+static void aue_task_sched(struct aue_softc *, int);
+static void aue_task(void *xsc, int pending);
+static void aue_tick(void *);
+static void aue_rxstart(struct ifnet *);
+static void aue_rxstart_thread(struct aue_softc *);
+static void aue_start(struct ifnet *);
+static void aue_start_thread(struct aue_softc *);
+static int aue_ioctl(struct ifnet *, u_long, caddr_t);
+static void aue_init(void *);
+static void aue_init_body(struct aue_softc *);
+static void aue_stop(struct aue_softc *);
+static void aue_watchdog(struct aue_softc *);
+static int aue_ifmedia_upd(struct ifnet *);
+static void aue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static void aue_eeprom_getword(struct aue_softc *, int, u_int16_t *);
+static void aue_read_eeprom(struct aue_softc *, caddr_t, int, int, int);
+
+static void aue_setmulti(struct aue_softc *);
+static void aue_reset(struct aue_softc *);
+
+static int aue_csr_read_1(struct aue_softc *, int);
+static int aue_csr_write_1(struct aue_softc *, int, int);
+static int aue_csr_read_2(struct aue_softc *, int);
+static int aue_csr_write_2(struct aue_softc *, int, int);
+
+static device_method_t aue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aue_match),
+ DEVMETHOD(device_attach, aue_attach),
+ DEVMETHOD(device_detach, aue_detach),
+ DEVMETHOD(device_shutdown, aue_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, aue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, aue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, aue_miibus_statchg),
+
+ { 0, 0 }
+};
+
+static driver_t aue_driver = {
+ "aue",
+ aue_methods,
+ sizeof(struct aue_softc)
+};
+
+static devclass_t aue_devclass;
+
+DRIVER_MODULE(aue, uhub, aue_driver, aue_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0);
+
+#define AUE_SETBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x))
+
+#define AUE_CLRBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x))
+
+static int
+aue_csr_read_1(struct aue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int8_t val = 0;
+
+ AUE_SXASSERTLOCKED(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ if (err) {
+ return (0);
+ }
+
+ return (val);
+}
+
+static int
+aue_csr_read_2(struct aue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int16_t val = 0;
+
+ AUE_SXASSERTLOCKED(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ if (err) {
+ return (0);
+ }
+
+ return (val);
+}
+
+static int
+aue_csr_write_1(struct aue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ AUE_SXASSERTLOCKED(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ if (err) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+aue_csr_write_2(struct aue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ AUE_SXASSERTLOCKED(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->aue_udev, &req, &val);
+
+ if (err) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static void
+aue_eeprom_getword(struct aue_softc *sc, int addr, u_int16_t *dest)
+{
+ int i;
+ u_int16_t word = 0;
+
+ aue_csr_write_1(sc, AUE_EE_REG, addr);
+ aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE)
+ break;
+ }
+
+ if (i == AUE_TIMEOUT) {
+ printf("aue%d: EEPROM read timed out\n",
+ sc->aue_unit);
+ }
+
+ word = aue_csr_read_2(sc, AUE_EE_DATA);
+ *dest = word;
+
+ return;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static void
+aue_read_eeprom(struct aue_softc *sc, caddr_t dest, int off, int cnt, int swap)
+{
+ int i;
+ u_int16_t word = 0, *ptr;
+
+ for (i = 0; i < cnt; i++) {
+ aue_eeprom_getword(sc, off + i, &word);
+ ptr = (u_int16_t *)(dest + (i * 2));
+ if (swap)
+ *ptr = ntohs(word);
+ else
+ *ptr = word;
+ }
+
+ return;
+}
+
+static int
+aue_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ int i;
+ u_int16_t val = 0;
+
+ /*
+ * The Am79C901 HomePNA PHY actually contains
+ * two transceivers: a 1Mbps HomePNA PHY and a
+ * 10Mbps full/half duplex ethernet PHY with
+ * NWAY autoneg. However in the ADMtek adapter,
+ * only the 1Mbps PHY is actually connected to
+ * anything, so we ignore the 10Mbps one. It
+ * happens to be configured for MII address 3,
+ * so we filter that out.
+ */
+ if (sc->aue_vendor == USB_VENDOR_ADMTEK &&
+ sc->aue_product == USB_PRODUCT_ADMTEK_PEGASUS) {
+ if (phy == 3)
+ return (0);
+#ifdef notdef
+ if (phy != 1)
+ return (0);
+#endif
+ }
+
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ }
+
+ if (i == AUE_TIMEOUT) {
+ printf("aue%d: MII read timed out\n", sc->aue_unit);
+ }
+
+ val = aue_csr_read_2(sc, AUE_PHY_DATA);
+
+ return (val);
+}
+
+static int
+aue_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ int i;
+
+ if (phy == 3)
+ return (0);
+
+ aue_csr_write_2(sc, AUE_PHY_DATA, data);
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ }
+
+ if (i == AUE_TIMEOUT) {
+ printf("aue%d: MII read timed out\n",
+ sc->aue_unit);
+ }
+
+ return(0);
+}
+
+static void
+aue_miibus_statchg(device_t dev)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) {
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+ } else {
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+ }
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+ else
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+
+ /*
+ * Set the LED modes on the LinkSys adapter.
+ * This turns on the 'dual link LED' bin in the auxmode
+ * register of the Broadcom PHY.
+ */
+ if (sc->aue_flags & LSYS) {
+ u_int16_t auxmode;
+ auxmode = aue_miibus_readreg(dev, 0, 0x1b);
+ aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04);
+ }
+
+ return;
+}
+
+#define AUE_BITS 6
+
+static void
+aue_setmulti(struct aue_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ u_int32_t h = 0, i;
+ u_int8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ AUE_SXASSERTLOCKED(sc);
+ ifp = sc->aue_ifp;
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+ return;
+ }
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_le(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1);
+ hashtbl[(h >> 3)] |= 1 << (h & 0x7);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /* write the hashtable */
+ for (i = 0; i < 8; i++)
+ aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]);
+
+ return;
+}
+
+static void
+aue_reset_pegasus_II(struct aue_softc *sc)
+{
+ /* Magic constants taken from Linux driver. */
+ aue_csr_write_1(sc, AUE_REG_1D, 0);
+ aue_csr_write_1(sc, AUE_REG_7B, 2);
+#if 0
+ if ((sc->aue_flags & HAS_HOME_PNA) && mii_mode)
+ aue_csr_write_1(sc, AUE_REG_81, 6);
+ else
+#endif
+ aue_csr_write_1(sc, AUE_REG_81, 2);
+}
+
+static void
+aue_reset(struct aue_softc *sc)
+{
+ int i;
+
+ AUE_SXASSERTLOCKED(sc);
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
+
+ for (i = 0; i < AUE_TIMEOUT; i++) {
+ if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ printf("aue%d: reset failed\n", sc->aue_unit);
+
+ /*
+ * The PHY(s) attached to the Pegasus chip may be held
+ * in reset until we flip on the GPIO outputs. Make sure
+ * to set the GPIO pins high so that the PHY(s) will
+ * be enabled.
+ *
+ * Note: We force all of the GPIO pins low first, *then*
+ * enable the ones we want.
+ */
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0);
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1);
+
+ if (sc->aue_flags & LSYS) {
+ /* Grrr. LinkSys has to be different from everyone else. */
+ aue_csr_write_1(sc, AUE_GPIO0,
+ AUE_GPIO_SEL0 | AUE_GPIO_SEL1);
+ aue_csr_write_1(sc, AUE_GPIO0,
+ AUE_GPIO_SEL0 | AUE_GPIO_SEL1 | AUE_GPIO_OUT0);
+ }
+
+ if (sc->aue_flags & PII)
+ aue_reset_pegasus_II(sc);
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(10000);
+
+ return;
+}
+
+/*
+ * Probe for a Pegasus chip.
+ */
+static int
+aue_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ /*
+ * Belkin USB Bluetooth dongles of the F8T012xx1 model series conflict
+ * with older Belkin USB2LAN adapters. Skip if_aue if we detect one of
+ * the devices that look like Bluetooth adapters.
+ */
+ if (uaa->vendor == USB_VENDOR_BELKIN &&
+ uaa->product == USB_PRODUCT_BELKIN_F8T012 && uaa->release == 0x0413)
+ return (UMATCH_NONE);
+
+ return (aue_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+aue_attach(device_t self)
+{
+ struct aue_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usbd_interface_handle iface;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ sc->aue_dev = self;
+ sc->aue_udev = uaa->device;
+ sc->aue_unit = device_get_unit(self);
+
+ if (usbd_set_config_no(sc->aue_udev, AUE_CONFIG_NO, 0)) {
+ device_printf(self, "getting interface handle failed\n");
+ return ENXIO;
+ }
+
+ err = usbd_device2interface_handle(uaa->device, AUE_IFACE_IDX, &iface);
+ if (err) {
+ device_printf(self, "getting interface handle failed\n");
+ return ENXIO;
+ }
+
+ sc->aue_iface = iface;
+ sc->aue_flags = aue_lookup(uaa->vendor, uaa->product)->aue_flags;
+
+ sc->aue_product = uaa->product;
+ sc->aue_vendor = uaa->vendor;
+
+ id = usbd_get_interface_descriptor(sc->aue_iface);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ device_printf(self, "couldn't get ep %d\n", i);
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->aue_ed[AUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->aue_ed[AUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->aue_ed[AUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ mtx_init(&sc->aue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ sx_init(&sc->aue_sx, device_get_nameunit(self));
+ TASK_INIT(&sc->aue_task, 0, aue_task, sc);
+ usb_ether_task_init(self, 0, &sc->aue_taskqueue);
+ AUE_SXLOCK(sc);
+
+ /* Reset the adapter. */
+ aue_reset(sc);
+
+ /*
+ * Get station address from the EEPROM.
+ */
+ aue_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0);
+
+ ifp = sc->aue_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(self, "can not if_alloc()\n");
+ AUE_SXUNLOCK(sc);
+ mtx_destroy(&sc->aue_mtx);
+ sx_destroy(&sc->aue_sx);
+ usb_ether_task_destroy(&sc->aue_taskqueue);
+ return ENXIO;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "aue", sc->aue_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = aue_ioctl;
+ ifp->if_start = aue_start;
+ ifp->if_init = aue_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ /*
+ * Do MII setup.
+ * NOTE: Doing this causes child devices to be attached to us,
+ * which we would normally disconnect at in the detach routine
+ * using device_delete_child(). However the USB code is set up
+ * such that when this driver is removed, all children devices
+ * are removed as well. In effect, the USB code ends up detaching
+ * all of our children for us, so we don't have to do is ourselves
+ * in aue_detach(). It's important to point this out since if
+ * we *do* try to detach the child devices ourselves, we will
+ * end up getting the children deleted twice, which will crash
+ * the system.
+ */
+ if (mii_phy_probe(self, &sc->aue_miibus,
+ aue_ifmedia_upd, aue_ifmedia_sts)) {
+ device_printf(self, "MII without any PHY!\n");
+ if_free(ifp);
+ AUE_SXUNLOCK(sc);
+ mtx_destroy(&sc->aue_mtx);
+ sx_destroy(&sc->aue_sx);
+ usb_ether_task_destroy(&sc->aue_taskqueue);
+ return ENXIO;
+ }
+
+ sc->aue_qdat.ifp = ifp;
+ sc->aue_qdat.if_rxstart = aue_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+ ether_ifattach(ifp, eaddr);
+ usb_register_netisr();
+ sc->aue_dying = 0;
+ sc->aue_link = 1;
+
+ AUE_SXUNLOCK(sc);
+ return 0;
+}
+
+static int
+aue_detach(device_t dev)
+{
+ struct aue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ AUE_SXLOCK(sc);
+ ifp = sc->aue_ifp;
+ ether_ifdetach(ifp);
+ sc->aue_dying = 1;
+ AUE_SXUNLOCK(sc);
+ callout_drain(&sc->aue_tick_callout);
+ usb_ether_task_drain(&sc->aue_taskqueue, &sc->aue_task);
+ usb_ether_task_destroy(&sc->aue_taskqueue);
+ if_free(ifp);
+
+ if (sc->aue_ep[AUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_TX]);
+ if (sc->aue_ep[AUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_RX]);
+#ifdef AUE_INTR_PIPE
+ if (sc->aue_ep[AUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_INTR]);
+#endif
+
+ mtx_destroy(&sc->aue_mtx);
+ sx_destroy(&sc->aue_sx);
+
+ return (0);
+}
+
+static void
+aue_rxstart(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ aue_task_sched(sc, AUE_TASK_RXSTART);
+}
+
+static void
+aue_rxstart_thread(struct aue_softc *sc)
+{
+ struct ue_chain *c;
+ struct ifnet *ifp;
+
+ ifp = sc->aue_ifp;
+
+ sc = ifp->if_softc;
+ AUE_SXASSERTLOCKED(sc);
+ c = &sc->aue_cdata.ue_rx_chain[sc->aue_cdata.ue_rx_prod];
+
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL) {
+ device_printf(sc->aue_dev, "no memory for rx list -- packet "
+ "dropped!\n");
+ ifp->if_ierrors++;
+ AUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->aue_ep[AUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, aue_rxeof);
+ usbd_transfer(c->ue_xfer);
+
+ return;
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void
+aue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ c->ue_status = status;
+ aue_task_sched(c->ue_sc, AUE_TASK_RXEOF);
+}
+
+static void
+aue_rxeof_thread(struct aue_softc *sc)
+{
+ struct ue_chain *c = &(sc->aue_cdata.ue_rx_chain[0]);
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ struct aue_rxpkt r;
+ usbd_status status = c->ue_status;
+
+
+ AUE_SXASSERTLOCKED(sc);
+ ifp = sc->aue_ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ return;
+ }
+ if (usbd_ratecheck(&sc->aue_rx_notice))
+ device_printf(sc->aue_dev, "usb error on rx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->aue_ep[AUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, &total_len, NULL);
+
+ if (total_len <= 4 + ETHER_CRC_LEN) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ m = c->ue_mbuf;
+ bcopy(mtod(m, char *) + total_len - 4, (char *)&r, sizeof(r));
+
+ /* Turn off all the non-error bits in the rx status word. */
+ r.aue_rxstat &= AUE_RXSTAT_MASK;
+
+ if (r.aue_rxstat) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ /* No errors; receive the packet. */
+ total_len -= (4 + ETHER_CRC_LEN);
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (void *)&sc->aue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ return;
+done:
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->aue_ep[AUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, aue_rxeof);
+ usbd_transfer(c->ue_xfer);
+
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+static void
+aue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ c->ue_status = status;
+ aue_task_sched(c->ue_sc, AUE_TASK_TXEOF);
+}
+
+static void
+aue_txeof_thread(struct aue_softc *sc)
+{
+ struct ue_chain *c = &(sc->aue_cdata.ue_tx_chain[0]);
+ struct ifnet *ifp;
+ usbd_status err, status;
+
+ AUE_SXASSERTLOCKED(sc);
+ status = c->ue_status;
+ ifp = sc->aue_ifp;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ return;
+ }
+ device_printf(sc->aue_dev, "usb error on tx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->aue_ep[AUE_ENDPT_TX]);
+ return;
+ }
+
+ sc->aue_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->ue_mbuf != NULL) {
+ c->ue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->ue_mbuf);
+ c->ue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ return;
+}
+
+static void
+aue_tick(void *xsc)
+{
+ struct aue_softc *sc = xsc;
+
+ aue_task_sched(sc, AUE_TASK_TICK);
+}
+
+static void
+aue_tick_thread(struct aue_softc *sc)
+{
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ AUE_SXASSERTLOCKED(sc);
+ ifp = sc->aue_ifp;
+ /*
+ * If a timer is set (non-zero) then decrement it
+ * and if it hits zero, then call the watchdog routine.
+ */
+ if (sc->aue_timer != 0 && --sc->aue_timer == 0) {
+ aue_watchdog(sc);
+ }
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ return;
+ }
+
+ mii = GET_MII(sc);
+ if (mii == NULL) {
+ goto resched;
+ }
+
+ mii_tick(mii);
+ if (!sc->aue_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->aue_link++;
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ aue_start_thread(sc);
+ }
+resched:
+ (void) callout_reset(&sc->aue_tick_callout, hz, aue_tick, sc);
+ return;
+}
+
+static int
+aue_encap(struct aue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct ue_chain *c;
+ usbd_status err;
+
+ AUE_SXASSERTLOCKED(sc);
+
+ c = &sc->aue_cdata.ue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2);
+ c->ue_mbuf = m;
+
+ total_len = m->m_pkthdr.len + 2;
+
+ /*
+ * The ADMtek documentation says that the packet length is
+ * supposed to be specified in the first two bytes of the
+ * transfer, however it actually seems to ignore this info
+ * and base the frame size on the bulk transfer length.
+ */
+ c->ue_buf[0] = (u_int8_t)m->m_pkthdr.len;
+ c->ue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8);
+
+ usbd_setup_xfer(c->ue_xfer, sc->aue_ep[AUE_ENDPT_TX],
+ c, c->ue_buf, total_len, USBD_FORCE_SHORT_XFER,
+ 10000, aue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->ue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ aue_stop(sc);
+ return (EIO);
+ }
+
+ sc->aue_cdata.ue_tx_cnt++;
+
+ return (0);
+}
+
+
+static void
+aue_start(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ aue_task_sched(sc, AUE_TASK_START);
+}
+
+static void
+aue_start_thread(struct aue_softc *sc)
+{
+ struct ifnet *ifp = sc->aue_ifp;
+ struct mbuf *m_head = NULL;
+
+ AUE_SXASSERTLOCKED(sc);
+
+ if (!sc->aue_link) {
+ return;
+ }
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ return;
+ }
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ return;
+ }
+
+ if (aue_encap(sc, m_head, 0)) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ sc->aue_timer = 5;
+
+ return;
+}
+
+static void
+aue_init(void *xsc)
+{
+ struct aue_softc *sc = xsc;
+
+ AUE_SXLOCK(sc);
+ aue_init_body(sc);
+ AUE_SXUNLOCK(sc);
+}
+
+static void
+aue_init_body(struct aue_softc *sc)
+{
+ struct ifnet *ifp = sc->aue_ifp;
+ struct mii_data *mii = GET_MII(sc);
+ struct ue_chain *c;
+ usbd_status err;
+ int i;
+
+ AUE_SXASSERTLOCKED(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ return;
+ }
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ aue_reset(sc);
+
+ /* Set MAC address */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ aue_csr_write_1(sc, AUE_PAR0 + i, IF_LLADDR(sc->aue_ifp)[i]);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ else
+ AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+
+ /* Init TX ring. */
+ if (usb_ether_tx_list_init(sc, &sc->aue_cdata,
+ sc->aue_udev) == ENOBUFS) {
+ device_printf(sc->aue_dev, "tx list init failed\n");
+ return;
+ }
+
+ /* Init RX ring. */
+ if (usb_ether_rx_list_init(sc, &sc->aue_cdata,
+ sc->aue_udev) == ENOBUFS) {
+ device_printf(sc->aue_dev, "rx list init failed\n");
+ return;
+ }
+
+
+ /* Load the multicast filter. */
+ aue_setmulti(sc);
+
+ /* Enable RX and TX */
+ aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
+
+ mii_mediachg(mii);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->aue_iface, sc->aue_ed[AUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->aue_ep[AUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->aue_dev, "open rx pipe failed: %s\n",
+ usbd_errstr(err));
+ return;
+ }
+ err = usbd_open_pipe(sc->aue_iface, sc->aue_ed[AUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->aue_ep[AUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->aue_dev, "open tx pipe failed: %s\n",
+ usbd_errstr(err));
+ return;
+ }
+
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ c = &sc->aue_cdata.ue_rx_chain[i];
+ usbd_setup_xfer(c->ue_xfer, sc->aue_ep[AUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, aue_rxeof);
+ usbd_transfer(c->ue_xfer);
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ callout_init(&sc->aue_tick_callout, CALLOUT_MPSAFE);
+ (void) callout_reset(&sc->aue_tick_callout, hz, aue_tick, sc);
+ return;
+}
+
+/*
+ * Set media options.
+ */
+static int
+aue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ sc->aue_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+ sc->aue_link = 1;
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ return;
+}
+
+static int
+aue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ int error = 0;
+
+ /*
+ * This prevents recursion in the interface while it's
+ * being torn down.
+ */
+ if (sc->aue_dying)
+ return(0);
+
+ AUE_GIANTLOCK();
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ AUE_SXLOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->aue_if_flags & IFF_PROMISC)) {
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->aue_if_flags & IFF_PROMISC) {
+ AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ aue_init_body(sc);
+ }
+ sc->aue_dying = 0;
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ aue_stop(sc);
+ }
+ sc->aue_if_flags = ifp->if_flags;
+ AUE_SXUNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ AUE_SXLOCK(sc);
+ aue_setmulti(sc);
+ AUE_SXUNLOCK(sc);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ AUE_SXLOCK(sc);
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ AUE_SXUNLOCK(sc);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ AUE_GIANTUNLOCK();
+
+ return (error);
+}
+
+static void
+aue_watchdog(struct aue_softc *sc)
+{
+ struct ifnet *ifp = sc->aue_ifp;
+ struct ue_chain *c;
+ usbd_status stat;
+
+ AUE_SXASSERTLOCKED(sc);
+ ifp->if_oerrors++;
+ device_printf(sc->aue_dev, "watchdog timeout\n");
+
+ c = &sc->aue_cdata.ue_tx_chain[0];
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat);
+ c->ue_status = stat;
+ aue_txeof_thread(sc);
+
+ if (!IFQ_IS_EMPTY(&ifp->if_snd))
+ aue_start_thread(sc);
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+aue_stop(struct aue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+
+ AUE_SXASSERTLOCKED(sc);
+ ifp = sc->aue_ifp;
+ sc->aue_timer = 0;
+
+ aue_csr_write_1(sc, AUE_CTL0, 0);
+ aue_csr_write_1(sc, AUE_CTL1, 0);
+ aue_reset(sc);
+ sc->aue_dying = 1;
+
+ /* Stop transfers. */
+ if (sc->aue_ep[AUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->aue_dev,
+ "abort rx pipe failed: %s\n", usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->aue_ep[AUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->aue_dev,
+ "close rx pipe failed: %s\n", usbd_errstr(err));
+ }
+ sc->aue_ep[AUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->aue_ep[AUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->aue_dev,
+ "abort tx pipe failed: %s\n", usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->aue_ep[AUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->aue_dev,
+ "close tx pipe failed: %s\n", usbd_errstr(err));
+ }
+ sc->aue_ep[AUE_ENDPT_TX] = NULL;
+ }
+
+#ifdef AUE_INTR_PIPE
+ if (sc->aue_ep[AUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->aue_ep[AUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->aue_dev,
+ "abort intr pipe failed: %s\n", usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->aue_ep[AUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->aue_dev,
+ "close intr pipe failed: %s\n", usbd_errstr(err));
+ }
+ sc->aue_ep[AUE_ENDPT_INTR] = NULL;
+ }
+#endif
+
+ /* Free RX resources. */
+ usb_ether_rx_list_free(&sc->aue_cdata);
+ /* Free TX resources. */
+ usb_ether_tx_list_free(&sc->aue_cdata);
+
+#ifdef AUE_INTR_PIPE
+ free(sc->aue_cdata.ue_ibuf, M_USBDEV);
+ sc->aue_cdata.ue_ibuf = NULL;
+#endif
+
+ sc->aue_link = 0;
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+aue_shutdown(device_t dev)
+{
+ struct aue_softc *sc;
+
+ sc = device_get_softc(dev);
+ AUE_SXLOCK(sc);
+ sc->aue_dying++;
+ aue_reset(sc);
+ aue_stop(sc);
+ AUE_SXUNLOCK(sc);
+
+ return (0);
+}
+
+static void
+aue_task_sched(struct aue_softc *sc, int task)
+{
+
+ AUE_LOCK(sc);
+ sc->aue_deferedtasks |= task;
+ usb_ether_task_enqueue(&sc->aue_taskqueue, &sc->aue_task);
+ AUE_UNLOCK(sc);
+}
+
+/*
+ * We defer all interrupt operations to this function.
+ *
+ * This allows us to do more complex operations, such as synchronous
+ * usb io that normally would not be allowed from interrupt context.
+ */
+static void
+aue_task(void *arg, int pending)
+{
+ struct aue_softc *sc = arg;
+ int tasks;
+
+ for ( ;; ) {
+ AUE_LOCK(sc);
+ tasks = sc->aue_deferedtasks;
+ sc->aue_deferedtasks = 0;
+ AUE_UNLOCK(sc);
+
+ if (tasks == 0)
+ break;
+
+ AUE_GIANTLOCK(); // XXX: usb not giant safe
+ AUE_SXLOCK(sc);
+ if (sc->aue_dying) {
+ AUE_SXUNLOCK(sc);
+ break;
+ }
+ if ((tasks & AUE_TASK_TICK) != 0) {
+ aue_tick_thread(sc);
+ }
+ if ((tasks & AUE_TASK_START) != 0) {
+ aue_start_thread(sc);
+ }
+ if ((tasks & AUE_TASK_RXSTART) != 0) {
+ aue_rxstart_thread(sc);
+ }
+ if ((tasks & AUE_TASK_RXEOF) != 0) {
+ aue_rxeof_thread(sc);
+ }
+ if ((tasks & AUE_TASK_TXEOF) != 0) {
+ aue_txeof_thread(sc);
+ }
+ AUE_SXUNLOCK(sc);
+ AUE_GIANTUNLOCK(); // XXX: usb not giant safe
+ }
+}
+
diff --git a/sys/legacy/dev/usb/if_auereg.h b/sys/legacy/dev/usb/if_auereg.h
new file mode 100644
index 0000000..18cc0f4
--- /dev/null
+++ b/sys/legacy/dev/usb/if_auereg.h
@@ -0,0 +1,294 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Copyright (c) 2006
+ * Alfred Perlstein <alfred@freebsd.org>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Register definitions for ADMtek Pegasus AN986 USB to Ethernet
+ * chip. The Pegasus uses a total of four USB endpoints: the control
+ * endpoint (0), a bulk read endpoint for receiving packets (1),
+ * a bulk write endpoint for sending packets (2) and an interrupt
+ * endpoint for passing RX and TX status (3). Endpoint 0 is used
+ * to read and write the ethernet module's registers. All registers
+ * are 8 bits wide.
+ *
+ * Packet transfer is done in 64 byte chunks. The last chunk in a
+ * transfer is denoted by having a length less that 64 bytes. For
+ * the RX case, the data includes an optional RX status word.
+ */
+
+#ifndef AUEREG_H
+#define AUEREG_H
+
+#define AUE_UR_READREG 0xF0
+#define AUE_UR_WRITEREG 0xF1
+
+#define AUE_CONFIG_NO 1
+#define AUE_IFACE_IDX 0
+
+/*
+ * Note that while the ADMtek technically has four
+ * endpoints, the control endpoint (endpoint 0) is
+ * regarded as special by the USB code and drivers
+ * don't have direct access to it. (We access it
+ * using usbd_do_request() when reading/writing
+ * registers.) Consequently, our endpoint indexes
+ * don't match those in the ADMtek Pegasus manual:
+ * we consider the RX data endpoint to be index 0
+ * and work up from there.
+ */
+#define AUE_ENDPT_RX 0x0
+#define AUE_ENDPT_TX 0x1
+#define AUE_ENDPT_INTR 0x2
+#define AUE_ENDPT_MAX 0x3
+
+#define AUE_INTR_PKTLEN 0x8
+
+#define AUE_CTL0 0x00
+#define AUE_CTL1 0x01
+#define AUE_CTL2 0x02
+#define AUE_MAR0 0x08
+#define AUE_MAR1 0x09
+#define AUE_MAR2 0x0A
+#define AUE_MAR3 0x0B
+#define AUE_MAR4 0x0C
+#define AUE_MAR5 0x0D
+#define AUE_MAR6 0x0E
+#define AUE_MAR7 0x0F
+#define AUE_MAR AUE_MAR0
+#define AUE_PAR0 0x10
+#define AUE_PAR1 0x11
+#define AUE_PAR2 0x12
+#define AUE_PAR3 0x13
+#define AUE_PAR4 0x14
+#define AUE_PAR5 0x15
+#define AUE_PAR AUE_PAR0
+#define AUE_PAUSE0 0x18
+#define AUE_PAUSE1 0x19
+#define AUE_PAUSE AUE_PAUSE0
+#define AUE_RX_FLOWCTL_CNT 0x1A
+#define AUE_RX_FLOWCTL_FIFO 0x1B
+#define AUE_REG_1D 0x1D
+#define AUE_EE_REG 0x20
+#define AUE_EE_DATA0 0x21
+#define AUE_EE_DATA1 0x22
+#define AUE_EE_DATA AUE_EE_DATA0
+#define AUE_EE_CTL 0x23
+#define AUE_PHY_ADDR 0x25
+#define AUE_PHY_DATA0 0x26
+#define AUE_PHY_DATA1 0x27
+#define AUE_PHY_DATA AUE_PHY_DATA0
+#define AUE_PHY_CTL 0x28
+#define AUE_USB_STS 0x2A
+#define AUE_TXSTAT0 0x2B
+#define AUE_TXSTAT1 0x2C
+#define AUE_TXSTAT AUE_TXSTAT0
+#define AUE_RXSTAT 0x2D
+#define AUE_PKTLOST0 0x2E
+#define AUE_PKTLOST1 0x2F
+#define AUE_PKTLOST AUE_PKTLOST0
+
+#define AUE_REG_7B 0x7B
+#define AUE_GPIO0 0x7E
+#define AUE_GPIO1 0x7F
+#define AUE_REG_81 0x81
+
+#define AUE_CTL0_INCLUDE_RXCRC 0x01
+#define AUE_CTL0_ALLMULTI 0x02
+#define AUE_CTL0_STOP_BACKOFF 0x04
+#define AUE_CTL0_RXSTAT_APPEND 0x08
+#define AUE_CTL0_WAKEON_ENB 0x10
+#define AUE_CTL0_RXPAUSE_ENB 0x20
+#define AUE_CTL0_RX_ENB 0x40
+#define AUE_CTL0_TX_ENB 0x80
+
+#define AUE_CTL1_HOMELAN 0x04
+#define AUE_CTL1_RESETMAC 0x08
+#define AUE_CTL1_SPEEDSEL 0x10 /* 0 = 10mbps, 1 = 100mbps */
+#define AUE_CTL1_DUPLEX 0x20 /* 0 = half, 1 = full */
+#define AUE_CTL1_DELAYHOME 0x40
+
+#define AUE_CTL2_EP3_CLR 0x01 /* reading EP3 clrs status regs */
+#define AUE_CTL2_RX_BADFRAMES 0x02
+#define AUE_CTL2_RX_PROMISC 0x04
+#define AUE_CTL2_LOOPBACK 0x08
+#define AUE_CTL2_EEPROMWR_ENB 0x10
+#define AUE_CTL2_EEPROM_LOAD 0x20
+
+#define AUE_EECTL_WRITE 0x01
+#define AUE_EECTL_READ 0x02
+#define AUE_EECTL_DONE 0x04
+
+#define AUE_PHYCTL_PHYREG 0x1F
+#define AUE_PHYCTL_WRITE 0x20
+#define AUE_PHYCTL_READ 0x40
+#define AUE_PHYCTL_DONE 0x80
+
+#define AUE_USBSTS_SUSPEND 0x01
+#define AUE_USBSTS_RESUME 0x02
+
+#define AUE_TXSTAT0_JABTIMO 0x04
+#define AUE_TXSTAT0_CARLOSS 0x08
+#define AUE_TXSTAT0_NOCARRIER 0x10
+#define AUE_TXSTAT0_LATECOLL 0x20
+#define AUE_TXSTAT0_EXCESSCOLL 0x40
+#define AUE_TXSTAT0_UNDERRUN 0x80
+
+#define AUE_TXSTAT1_PKTCNT 0x0F
+#define AUE_TXSTAT1_FIFO_EMPTY 0x40
+#define AUE_TXSTAT1_FIFO_FULL 0x80
+
+#define AUE_RXSTAT_OVERRUN 0x01
+#define AUE_RXSTAT_PAUSE 0x02
+
+#define AUE_GPIO_IN0 0x01
+#define AUE_GPIO_OUT0 0x02
+#define AUE_GPIO_SEL0 0x04
+#define AUE_GPIO_IN1 0x08
+#define AUE_GPIO_OUT1 0x10
+#define AUE_GPIO_SEL1 0x20
+
+struct aue_intrpkt {
+ u_int8_t aue_txstat0;
+ u_int8_t aue_txstat1;
+ u_int8_t aue_rxstat;
+ u_int8_t aue_rxlostpkt0;
+ u_int8_t aue_rxlostpkt1;
+ u_int8_t aue_wakeupstat;
+ u_int8_t aue_rsvd;
+};
+
+struct aue_rxpkt {
+ u_int16_t aue_pktlen;
+ u_int8_t aue_rxstat;
+};
+
+#define AUE_RXSTAT_MCAST 0x01
+#define AUE_RXSTAT_GIANT 0x02
+#define AUE_RXSTAT_RUNT 0x04
+#define AUE_RXSTAT_CRCERR 0x08
+#define AUE_RXSTAT_DRIBBLE 0x10
+#define AUE_RXSTAT_MASK 0x1E
+
+#define AUE_INC(x, y) (x) = (x + 1) % y
+
+struct aue_softc {
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->aue_miibus))
+#elif defined(__NetBSD__)
+#define GET_MII(sc) (&(sc)->aue_mii)
+#elif defined(__OpenBSD__)
+#define GET_MII(sc) (&(sc)->aue_mii)
+#endif
+ struct ifnet *aue_ifp;
+ device_t aue_dev;
+ device_t aue_miibus;
+ usbd_device_handle aue_udev;
+ usbd_interface_handle aue_iface;
+ u_int16_t aue_vendor;
+ u_int16_t aue_product;
+ int aue_ed[AUE_ENDPT_MAX];
+ usbd_pipe_handle aue_ep[AUE_ENDPT_MAX];
+ int aue_unit;
+ u_int8_t aue_link;
+ int aue_timer;
+ int aue_if_flags;
+ struct ue_cdata aue_cdata;
+ struct callout aue_tick_callout;
+ struct usb_taskqueue aue_taskqueue;
+ struct task aue_task;
+ struct mtx aue_mtx;
+ struct sx aue_sx;
+ u_int16_t aue_flags;
+ char aue_dying;
+ struct timeval aue_rx_notice;
+ struct usb_qdat aue_qdat;
+ int aue_deferedtasks;
+};
+
+#if 0
+/*
+ * Some debug code to make sure we don't take a blocking lock in
+ * interrupt context.
+ */
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/kdb.h>
+
+#define AUE_DUMPSTATE(tag) aue_dumpstate(__func__, tag)
+
+static inline void
+aue_dumpstate(const char *func, const char *tag)
+{
+ if ((curthread->td_pflags & TDP_NOSLEEPING) ||
+ (curthread->td_pflags & TDP_ITHREAD)) {
+ kdb_backtrace();
+ printf("%s: %s sleep: %sok ithread: %s\n", func, tag,
+ curthread->td_pflags & TDP_NOSLEEPING ? "not" : "",
+ curthread->td_pflags & TDP_ITHREAD ? "yes" : "no");
+ }
+}
+#else
+#define AUE_DUMPSTATE(tag)
+#endif
+
+#define AUE_LOCK(_sc) mtx_lock(&(_sc)->aue_mtx)
+#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->aue_mtx)
+#define AUE_SXLOCK(_sc) \
+ do { AUE_DUMPSTATE("sxlock"); sx_xlock(&(_sc)->aue_sx); } while(0)
+#define AUE_SXUNLOCK(_sc) sx_xunlock(&(_sc)->aue_sx)
+#define AUE_SXASSERTLOCKED(_sc) sx_assert(&(_sc)->aue_sx, SX_XLOCKED)
+#define AUE_SXASSERTUNLOCKED(_sc) sx_assert(&(_sc)->aue_sx, SX_UNLOCKED)
+
+#define AUE_TIMEOUT 1000
+#define AUE_MIN_FRAMELEN 60
+#define AUE_INTR_INTERVAL 100 /* ms */
+
+/*
+ * These bits are used to notify the task about pending events.
+ * The names correspond to the interrupt context routines that would
+ * be normally called. (example: AUE_TASK_WATCHDOG -> aue_watchdog())
+ */
+#define AUE_TASK_WATCHDOG 0x0001
+#define AUE_TASK_TICK 0x0002
+#define AUE_TASK_START 0x0004
+#define AUE_TASK_RXSTART 0x0008
+#define AUE_TASK_RXEOF 0x0010
+#define AUE_TASK_TXEOF 0x0020
+
+#define AUE_GIANTLOCK() mtx_lock(&Giant);
+#define AUE_GIANTUNLOCK() mtx_unlock(&Giant);
+
+#endif /* !AUEREG_H */
diff --git a/sys/legacy/dev/usb/if_axe.c b/sys/legacy/dev/usb/if_axe.c
new file mode 100644
index 0000000..65da32b
--- /dev/null
+++ b/sys/legacy/dev/usb/if_axe.c
@@ -0,0 +1,1428 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver.
+ * Used in the LinkSys USB200M and various other adapters.
+ *
+ * Manuals available from:
+ * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF
+ * Note: you need the manual for the AX88170 chip (USB 1.x ethernet
+ * controller) to find the definitions for the RX control register.
+ * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF
+ *
+ * Written by Bill Paul <wpaul@windriver.com>
+ * Senior Engineer
+ * Wind River Systems
+ */
+
+/*
+ * The AX88172 provides USB ethernet supports at 10 and 100Mbps.
+ * It uses an external PHY (reference designs use a RealTek chip),
+ * and has a 64-bit multicast hash filter. There is some information
+ * missing from the manual which one needs to know in order to make
+ * the chip function:
+ *
+ * - You must set bit 7 in the RX control register, otherwise the
+ * chip won't receive any packets.
+ * - You must initialize all 3 IPG registers, or you won't be able
+ * to send any packets.
+ *
+ * Note that this device appears to only support loading the station
+ * address via autload from the EEPROM (i.e. there's no way to manaully
+ * set it).
+ *
+ * (Adam Weinberger wanted me to name this driver if_gir.c.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sx.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+/* "device miibus" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+/*
+ * AXE_178_MAX_FRAME_BURST
+ * max frame burst size for Ax88178 and Ax88772
+ * 0 2048 bytes
+ * 1 4096 bytes
+ * 2 8192 bytes
+ * 3 16384 bytes
+ * use the largest your system can handle without usb stalling.
+ *
+ * NB: 88772 parts appear to generate lots of input errors with
+ * a 2K rx buffer and 8K is only slightly faster than 4K on an
+ * EHCI port on a T42 so change at your own risk.
+ */
+#define AXE_178_MAX_FRAME_BURST 1
+
+#include <dev/usb/if_axereg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+const struct axe_type axe_devs[] = {
+ { { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200}, 0 },
+ { { USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2}, 0 },
+ { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET}, AX772 },
+ { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172}, 0 },
+ { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772}, AX772 },
+ { { USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178}, AX178 },
+ { { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T}, 0 },
+ { { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055 }, AX178 },
+ { { USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR}, 0},
+ { { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772 },
+ { { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX }, 0},
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100}, 0 },
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1 }, AX772 },
+ { { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E}, 0 },
+ { { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2 }, AX178 },
+ { { USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1}, 0 },
+ { { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M}, 0 },
+ { { USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000 }, AX178 },
+ { { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX}, 0 },
+ { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120}, 0 },
+ { { USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS }, AX772 },
+ { { USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T }, AX178 },
+ { { USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 },
+ { { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029}, 0 },
+ { { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028 }, AX178 }
+};
+
+#define axe_lookup(v, p) ((const struct axe_type *)usb_lookup(axe_devs, v, p))
+
+static device_probe_t axe_match;
+static device_attach_t axe_attach;
+static device_detach_t axe_detach;
+static device_shutdown_t axe_shutdown;
+static miibus_readreg_t axe_miibus_readreg;
+static miibus_writereg_t axe_miibus_writereg;
+static miibus_statchg_t axe_miibus_statchg;
+
+static int axe_encap(struct axe_softc *, struct mbuf *, int);
+static void axe_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void axe_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void axe_tick(void *);
+static void axe_tick_task(void *);
+static void axe_start(struct ifnet *);
+static int axe_ioctl(struct ifnet *, u_long, caddr_t);
+static void axe_init(void *);
+static void axe_stop(struct axe_softc *);
+static void axe_watchdog(struct ifnet *);
+static int axe_cmd(struct axe_softc *, int, int, int, void *);
+static int axe_ifmedia_upd(struct ifnet *);
+static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static void axe_setmulti(struct axe_softc *);
+
+static device_method_t axe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, axe_match),
+ DEVMETHOD(device_attach, axe_attach),
+ DEVMETHOD(device_detach, axe_detach),
+ DEVMETHOD(device_shutdown, axe_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, axe_miibus_readreg),
+ DEVMETHOD(miibus_writereg, axe_miibus_writereg),
+ DEVMETHOD(miibus_statchg, axe_miibus_statchg),
+
+ { 0, 0 }
+};
+
+static driver_t axe_driver = {
+ "axe",
+ axe_methods,
+ sizeof(struct axe_softc)
+};
+
+static devclass_t axe_devclass;
+
+DRIVER_MODULE(axe, uhub, axe_driver, axe_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(axe, usb, 1, 1, 1);
+MODULE_DEPEND(axe, miibus, 1, 1, 1);
+
+static int
+axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ AXE_SLEEPLOCKASSERT(sc);
+ if (sc->axe_dying)
+ return(0);
+
+ if (AXE_CMD_DIR(cmd))
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AXE_CMD_CMD(cmd);
+ USETW(req.wValue, val);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, AXE_CMD_LEN(cmd));
+
+ err = usbd_do_request(sc->axe_udev, &req, buf);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+
+static int
+axe_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ usbd_status err;
+ u_int16_t val;
+
+ if (sc->axe_dying)
+ return(0);
+
+ AXE_SLEEPLOCKASSERT(sc);
+#ifdef notdef
+ /*
+ * The chip tells us the MII address of any supported
+ * PHYs attached to the chip, so only read from those.
+ */
+
+ if (sc->axe_phyaddrs[0] != AXE_NOPHY && phy != sc->axe_phyaddrs[0])
+ return (0);
+
+ if (sc->axe_phyaddrs[1] != AXE_NOPHY && phy != sc->axe_phyaddrs[1])
+ return (0);
+#endif
+ if (sc->axe_phyaddrs[0] != 0xFF && sc->axe_phyaddrs[0] != phy)
+ return (0);
+
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, (void *)&val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+ AXE_UNLOCK(sc);
+
+ if (err) {
+ device_printf(sc->axe_dev, "read PHY failed\n");
+ return(-1);
+ }
+
+ if (val && val != 0xffff)
+ sc->axe_phyaddrs[0] = phy;
+
+ return (le16toh(val));
+}
+
+static int
+axe_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ usbd_status err;
+
+ if (sc->axe_dying)
+ return(0);
+
+ AXE_SLEEPLOCKASSERT(sc);
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ val = htole32(val);
+ err = axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, (void *)&val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+ AXE_UNLOCK(sc);
+
+ if (err) {
+ device_printf(sc->axe_dev, "write PHY failed\n");
+ return(-1);
+ }
+
+ return (0);
+}
+
+static void
+axe_miibus_statchg(device_t dev)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ int val, err;
+
+ val = (mii->mii_media_active & IFM_GMASK) == IFM_FDX ?
+ AXE_MEDIA_FULL_DUPLEX : 0;
+ if (sc->axe_flags & (AX178|AX772)) {
+ val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC;
+
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_1000_T:
+ val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK;
+ break;
+ case IFM_100_TX:
+ val |= AXE_178_MEDIA_100TX;
+ break;
+ case IFM_10_T:
+ /* doesn't need to be handled */
+ break;
+ }
+ }
+ err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL);
+ if (err)
+ device_printf(dev, "media change failed, error %d\n", err);
+}
+
+/*
+ * Set media options.
+ */
+static int
+axe_ifmedia_upd(struct ifnet *ifp)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ sc->axe_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ return;
+}
+
+static void
+axe_setmulti(struct axe_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ u_int32_t h = 0;
+ u_int16_t rxmode;
+ u_int8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ ifp = sc->axe_ifp;
+
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, (void *)&rxmode);
+ rxmode = le16toh(rxmode);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxmode |= AXE_RXCMD_ALLMULTI;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+ return;
+ } else
+ rxmode &= ~AXE_RXCMD_ALLMULTI;
+
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ hashtbl[h / 8] |= 1 << (h % 8);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+axe_ax88178_init(struct axe_softc *sc)
+{
+ int gpio0 = 0, phymode = 0;
+ u_int16_t eeprom;
+
+ axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL);
+ /* XXX magic */
+ axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom);
+ eeprom = le16toh(eeprom);
+ axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL);
+
+ /* if EEPROM is invalid we have to use to GPIO0 */
+ if (eeprom == 0xffff) {
+ phymode = 0;
+ gpio0 = 1;
+ } else {
+ phymode = eeprom & 7;
+ gpio0 = (eeprom & 0x80) ? 0 : 1;
+ }
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL);
+ usbd_delay_ms(sc->axe_udev, 40);
+ if ((eeprom >> 8) != 1) {
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
+ usbd_delay_ms(sc->axe_udev, 30);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL);
+ usbd_delay_ms(sc->axe_udev, 300);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
+ usbd_delay_ms(sc->axe_udev, 30);
+ } else {
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL);
+ usbd_delay_ms(sc->axe_udev, 30);
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL);
+ usbd_delay_ms(sc->axe_udev, 30);
+ }
+
+ /* soft reset */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, 0, NULL);
+ usbd_delay_ms(sc->axe_udev, 150);
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL);
+ usbd_delay_ms(sc->axe_udev, 150);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_ax88772_init(struct axe_softc *sc)
+{
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL);
+ usbd_delay_ms(sc->axe_udev, 40);
+
+ if (sc->axe_phyaddrs[1] == AXE_INTPHY) {
+ /* ask for embedded PHY */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL);
+ usbd_delay_ms(sc->axe_udev, 10);
+
+ /* power down and reset state, pin reset state */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
+ usbd_delay_ms(sc->axe_udev, 60);
+
+ /* power down/reset state, pin operating state */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
+ usbd_delay_ms(sc->axe_udev, 150);
+
+ /* power up, reset */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL);
+
+ /* power up, operating */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL);
+ } else {
+ /* ask for external PHY */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL);
+ usbd_delay_ms(sc->axe_udev, 10);
+
+ /* power down/reset state, pin operating state */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
+ }
+
+ usbd_delay_ms(sc->axe_udev, 150);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_reset(struct axe_softc *sc)
+{
+ if (sc->axe_dying)
+ return;
+
+ if (usbd_set_config_no(sc->axe_udev, AXE_CONFIG_NO, 1) ||
+ usbd_device2interface_handle(sc->axe_udev, AXE_IFACE_IDX,
+ &sc->axe_iface)) {
+ device_printf(sc->axe_dev, "getting interface handle failed\n");
+ }
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+ return;
+}
+
+/*
+ * Probe for a AX88172 chip.
+ */
+static int
+axe_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (!uaa->iface)
+ return(UMATCH_NONE);
+ return (axe_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+axe_attach(device_t self)
+{
+ struct axe_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ const struct axe_type *type;
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ sc->axe_udev = uaa->device;
+ sc->axe_dev = self;
+ type = axe_lookup(uaa->vendor, uaa->product);
+ if (type != NULL)
+ sc->axe_flags = type->axe_flags;
+
+ if (usbd_set_config_no(sc->axe_udev, AXE_CONFIG_NO, 1)) {
+ device_printf(sc->axe_dev, "getting interface handle failed\n");
+ return ENXIO;
+ }
+
+ usb_init_task(&sc->axe_tick_task, axe_tick_task, sc);
+
+ if (usbd_device2interface_handle(uaa->device,
+ AXE_IFACE_IDX, &sc->axe_iface)) {
+ device_printf(sc->axe_dev, "getting interface handle failed\n");
+ return ENXIO;
+ }
+
+ sc->axe_boundary = 64;
+ if (sc->axe_flags & (AX178|AX772)) {
+ if (sc->axe_udev->speed == USB_SPEED_HIGH) {
+ sc->axe_bufsz = AXE_178_MAX_BUFSZ;
+ sc->axe_boundary = 512;
+ } else
+ sc->axe_bufsz = AXE_178_MIN_BUFSZ;
+ } else
+ sc->axe_bufsz = AXE_172_BUFSZ;
+{ /* XXX debug */
+device_printf(sc->axe_dev, "%s, bufsz %d, boundary %d\n",
+ sc->axe_flags & AX178 ? "AX88178" :
+ sc->axe_flags & AX772 ? "AX88772" : "AX88172",
+ sc->axe_bufsz, sc->axe_boundary);
+}
+
+ id = usbd_get_interface_descriptor(sc->axe_iface);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->axe_iface, i);
+ if (!ed) {
+ device_printf(sc->axe_dev, "couldn't get ep %d\n", i);
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->axe_ed[AXE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->axe_ed[AXE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->axe_ed[AXE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ mtx_init(&sc->axe_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ sx_init(&sc->axe_sleeplock, device_get_nameunit(self));
+ AXE_SLEEPLOCK(sc);
+ AXE_LOCK(sc);
+
+ /* We need the PHYID for the init dance in some cases */
+ axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, (void *)&sc->axe_phyaddrs);
+
+ if (sc->axe_flags & AX178)
+ axe_ax88178_init(sc);
+ else if (sc->axe_flags & AX772)
+ axe_ax88772_init(sc);
+
+ /*
+ * Get station address.
+ */
+ if (sc->axe_flags & (AX178|AX772))
+ axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, &eaddr);
+ else
+ axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, &eaddr);
+
+ /*
+ * Fetch IPG values.
+ */
+ axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, (void *)&sc->axe_ipgs);
+
+ /*
+ * Work around broken adapters that appear to lie about
+ * their PHY addresses.
+ */
+ sc->axe_phyaddrs[0] = sc->axe_phyaddrs[1] = 0xFF;
+
+ ifp = sc->axe_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(sc->axe_dev, "can not if_alloc()\n");
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+ sx_destroy(&sc->axe_sleeplock);
+ mtx_destroy(&sc->axe_mtx);
+ return ENXIO;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "axe", device_get_unit(sc->axe_dev));
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT;
+ ifp->if_ioctl = axe_ioctl;
+ ifp->if_start = axe_start;
+ ifp->if_watchdog = axe_watchdog;
+ ifp->if_init = axe_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ if (mii_phy_probe(self, &sc->axe_miibus,
+ axe_ifmedia_upd, axe_ifmedia_sts)) {
+ device_printf(sc->axe_dev, "MII without any PHY!\n");
+ if_free(ifp);
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+ sx_destroy(&sc->axe_sleeplock);
+ mtx_destroy(&sc->axe_mtx);
+ return ENXIO;
+ }
+
+ /*
+ * Call MI attach routine.
+ */
+
+ ether_ifattach(ifp, eaddr);
+ callout_handle_init(&sc->axe_stat_ch);
+ usb_register_netisr();
+
+ sc->axe_dying = 0;
+
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+
+ return 0;
+}
+
+static int
+axe_detach(device_t dev)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ AXE_LOCK(sc);
+ ifp = sc->axe_ifp;
+
+ sc->axe_dying = 1;
+ untimeout(axe_tick, sc, sc->axe_stat_ch);
+ usb_rem_task(sc->axe_udev, &sc->axe_tick_task);
+
+ ether_ifdetach(ifp);
+ if_free(ifp);
+
+ if (sc->axe_ep[AXE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]);
+ if (sc->axe_ep[AXE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]);
+ if (sc->axe_ep[AXE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
+
+ AXE_UNLOCK(sc);
+ sx_destroy(&sc->axe_sleeplock);
+ mtx_destroy(&sc->axe_mtx);
+
+ return(0);
+}
+
+static int
+axe_rx_list_init(struct axe_softc *sc)
+{
+ struct axe_cdata *cd;
+ struct axe_chain *c;
+ int i;
+
+ cd = &sc->axe_cdata;
+ for (i = 0; i < AXE_RX_LIST_CNT; i++) {
+ c = &cd->axe_rx_chain[i];
+ c->axe_sc = sc;
+ c->axe_idx = i;
+ c->axe_mbuf = NULL;
+ if (c->axe_xfer == NULL) {
+ c->axe_xfer = usbd_alloc_xfer(sc->axe_udev);
+ if (c->axe_xfer == NULL)
+ return (ENOBUFS);
+ c->axe_buf = usbd_alloc_buffer(c->axe_xfer,
+ sc->axe_bufsz);
+ if (c->axe_buf == NULL) {
+ usbd_free_xfer(c->axe_xfer);
+ return (ENOBUFS);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static void
+axe_rx_list_free(struct axe_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < AXE_RX_LIST_CNT; i++) {
+ if (sc->axe_cdata.axe_rx_chain[i].axe_mbuf != NULL) {
+ m_freem(sc->axe_cdata.axe_rx_chain[i].axe_mbuf);
+ sc->axe_cdata.axe_rx_chain[i].axe_mbuf = NULL;
+ }
+ if (sc->axe_cdata.axe_rx_chain[i].axe_xfer != NULL) {
+ usbd_free_xfer(sc->axe_cdata.axe_rx_chain[i].axe_xfer);
+ sc->axe_cdata.axe_rx_chain[i].axe_xfer = NULL;
+ }
+ }
+}
+
+static int
+axe_tx_list_init(struct axe_softc *sc)
+{
+ struct axe_cdata *cd;
+ struct axe_chain *c;
+ int i;
+
+ cd = &sc->axe_cdata;
+ for (i = 0; i < AXE_TX_LIST_CNT; i++) {
+ c = &cd->axe_tx_chain[i];
+ c->axe_sc = sc;
+ c->axe_idx = i;
+ c->axe_mbuf = NULL;
+ if (c->axe_xfer == NULL) {
+ c->axe_xfer = usbd_alloc_xfer(sc->axe_udev);
+ if (c->axe_xfer == NULL)
+ return (ENOBUFS);
+ c->axe_buf = usbd_alloc_buffer(c->axe_xfer,
+ sc->axe_bufsz);
+ if (c->axe_buf == NULL) {
+ usbd_free_xfer(c->axe_xfer);
+ return (ENOBUFS);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static void
+axe_tx_list_free(struct axe_softc *sc)
+{
+ int i;
+
+ /* Free TX resources. */
+ for (i = 0; i < AXE_TX_LIST_CNT; i++) {
+ if (sc->axe_cdata.axe_tx_chain[i].axe_mbuf != NULL) {
+ m_freem(sc->axe_cdata.axe_tx_chain[i].axe_mbuf);
+ sc->axe_cdata.axe_tx_chain[i].axe_mbuf = NULL;
+ }
+ if (sc->axe_cdata.axe_tx_chain[i].axe_xfer != NULL) {
+ usbd_free_xfer(sc->axe_cdata.axe_tx_chain[i].axe_xfer);
+ sc->axe_cdata.axe_tx_chain[i].axe_xfer = NULL;
+ }
+ }
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void
+axe_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct axe_softc *sc;
+ struct axe_chain *c = (struct axe_chain *) priv;
+ struct mbuf *m;
+ u_char *buf;
+ struct ifnet *ifp;
+ struct axe_sframe_hdr *hdr;
+ int total_len = 0;
+ int pktlen = 0;
+
+ sc = c->axe_sc;
+ AXE_LOCK(sc);
+ ifp = sc->axe_ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->axe_rx_notice))
+ device_printf(sc->axe_dev, "usb error on rx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->axe_ep[AXE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ buf = c->axe_buf;
+
+ do {
+ if (sc->axe_flags & (AX178|AX772)) {
+ if (total_len < sizeof(struct axe_sframe_hdr)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+ if ((pktlen % 2) != 0)
+ pktlen++;
+ buf += pktlen;
+
+ hdr = (struct axe_sframe_hdr *) buf;
+ total_len -= sizeof(struct axe_sframe_hdr);
+ if ((hdr->len ^ hdr->ilen) != 0xffff) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+ pktlen = le16toh(hdr->len);
+ if (pktlen > total_len) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ buf += sizeof(struct axe_sframe_hdr);
+ total_len -= pktlen + (pktlen % 2);
+ } else {
+ pktlen = total_len;
+ total_len = 0;
+ }
+
+ if (pktlen < sizeof(struct ether_header)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+ m->m_data += ETHER_ALIGN;
+ memcpy(mtod(m, void *), buf, pktlen);
+ m->m_pkthdr.len = m->m_len = pktlen;
+ m->m_pkthdr.rcvif = ifp;
+
+ ifp->if_input(ifp, m);
+ ifp->if_ipackets++;
+ } while (total_len > 0);
+ /* fall thru... */
+done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(xfer, sc->axe_ep[AXE_ENDPT_RX],
+ c, c->axe_buf, sc->axe_bufsz, USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, axe_rxeof);
+ usbd_transfer(xfer);
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+static void
+axe_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct axe_softc *sc;
+ struct axe_chain *c;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ c = priv;
+ sc = c->axe_sc;
+ AXE_LOCK(sc);
+ ifp = sc->axe_ifp;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+ device_printf(sc->axe_dev, "usb error on tx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->axe_ep[AXE_ENDPT_TX]);
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ usbd_get_xfer_status(c->axe_xfer, NULL, NULL, NULL, &err);
+
+ if (c->axe_mbuf != NULL) {
+ m_freem(c->axe_mbuf);
+ c->axe_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ AXE_UNLOCK(sc);
+
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ axe_start(ifp);
+
+ return;
+}
+
+static void
+axe_tick(void *xsc)
+{
+ struct axe_softc *sc = xsc;
+
+ if (sc == NULL)
+ return;
+ if (sc->axe_dying)
+ return;
+
+ /* Perform periodic stuff in process context */
+ usb_add_task(sc->axe_udev, &sc->axe_tick_task, USB_TASKQ_DRIVER);
+}
+
+static void
+axe_tick_task(void *xsc)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ sc = xsc;
+
+ if (sc == NULL)
+ return;
+
+ AXE_SLEEPLOCK(sc);
+ AXE_LOCK(sc);
+
+ ifp = sc->axe_ifp;
+ mii = GET_MII(sc);
+ if (mii == NULL) {
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+ return;
+ }
+
+ mii_tick(mii);
+ if (!sc->axe_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->axe_link++;
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ axe_start(ifp);
+ }
+
+ sc->axe_stat_ch = timeout(axe_tick, sc, hz);
+
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+
+ return;
+}
+
+static int
+axe_encap(struct axe_softc *sc, struct mbuf *m, int idx)
+{
+ struct axe_chain *c;
+ usbd_status err;
+ struct axe_sframe_hdr hdr;
+ int length;
+
+ c = &sc->axe_cdata.axe_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ if (sc->axe_flags & (AX178|AX772)) {
+ hdr.len = htole16(m->m_pkthdr.len);
+ hdr.ilen = ~hdr.len;
+
+ memcpy(c->axe_buf, &hdr, sizeof(hdr));
+ length = sizeof(hdr);
+
+ m_copydata(m, 0, m->m_pkthdr.len, c->axe_buf + length);
+ length += m->m_pkthdr.len;
+
+ if ((length % sc->axe_boundary) == 0) {
+ hdr.len = 0;
+ hdr.ilen = 0xffff;
+ memcpy(c->axe_buf + length, &hdr, sizeof(hdr));
+ length += sizeof(hdr);
+ }
+ } else {
+ m_copydata(m, 0, m->m_pkthdr.len, c->axe_buf);
+ length = m->m_pkthdr.len;
+ }
+ c->axe_mbuf = m;
+
+ usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_TX],
+ c, c->axe_buf, length, USBD_FORCE_SHORT_XFER, 10000, axe_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->axe_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ /* XXX probably don't want to sleep here */
+ AXE_SLEEPLOCK(sc);
+ axe_stop(sc);
+ AXE_SLEEPUNLOCK(sc);
+ return(EIO);
+ }
+
+ sc->axe_cdata.axe_tx_cnt++;
+
+ return(0);
+}
+
+static void
+axe_start(struct ifnet *ifp)
+{
+ struct axe_softc *sc;
+ struct mbuf *m_head = NULL;
+
+ sc = ifp->if_softc;
+ AXE_LOCK(sc);
+
+ if (!sc->axe_link) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ if (axe_encap(sc, m_head, 0)) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ AXE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+axe_init(void *xsc)
+{
+ struct axe_softc *sc = xsc;
+ struct ifnet *ifp = sc->axe_ifp;
+ struct axe_chain *c;
+ usbd_status err;
+ int i;
+ int rxmode;
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ return;
+
+ AXE_SLEEPLOCK(sc);
+ AXE_LOCK(sc);
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+
+ axe_reset(sc);
+
+#ifdef notdef
+ /* Set MAC address */
+ axe_mac(sc, IF_LLADDR(sc->axe_ifp), 1);
+#endif
+
+ /* Enable RX logic. */
+
+ /* Init TX ring. */
+ if (axe_tx_list_init(sc) == ENOBUFS) {
+ device_printf(sc->axe_dev, "tx list init failed\n");
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (axe_rx_list_init(sc) == ENOBUFS) {
+ device_printf(sc->axe_dev, "rx list init failed\n");
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+ return;
+ }
+
+ /* Set transmitter IPG values */
+ if (sc->axe_flags & (AX178|AX772)) {
+ axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->axe_ipgs[2],
+ (sc->axe_ipgs[1]<<8) | sc->axe_ipgs[0], NULL);
+ } else {
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL);
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL);
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL);
+ }
+
+ /* Enable receiver, set RX mode */
+ rxmode = AXE_RXCMD_MULTICAST|AXE_RXCMD_ENABLE;
+ if (sc->axe_flags & (AX178|AX772)) {
+ if (sc->axe_bufsz == AXE_178_MAX_BUFSZ)
+ rxmode |= AXE_178_RXCMD_MFB;
+ } else
+ rxmode |= AXE_172_RXCMD_UNICAST;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= AXE_RXCMD_PROMISC;
+
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxmode |= AXE_RXCMD_BROADCAST;
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+
+ /* Load the multicast filter. */
+ axe_setmulti(sc);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->axe_dev, "open rx pipe failed: %s\n",
+ usbd_errstr(err));
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+ return;
+ }
+
+ err = usbd_open_pipe(sc->axe_iface, sc->axe_ed[AXE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->axe_ep[AXE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->axe_dev, "open tx pipe failed: %s\n",
+ usbd_errstr(err));
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+ return;
+ }
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < AXE_RX_LIST_CNT; i++) {
+ c = &sc->axe_cdata.axe_rx_chain[i];
+ usbd_setup_xfer(c->axe_xfer, sc->axe_ep[AXE_ENDPT_RX],
+ c, c->axe_buf, sc->axe_bufsz,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, axe_rxeof);
+ usbd_transfer(c->axe_xfer);
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ AXE_UNLOCK(sc);
+ AXE_SLEEPUNLOCK(sc);
+
+ sc->axe_stat_ch = timeout(axe_tick, sc, hz);
+
+ return;
+}
+
+static int
+axe_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ u_int16_t rxmode;
+ int error = 0;
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->axe_if_flags & IFF_PROMISC)) {
+ AXE_SLEEPLOCK(sc);
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_RXCTL_READ,
+ 0, 0, (void *)&rxmode);
+ rxmode = le16toh(rxmode);
+ rxmode |= AXE_RXCMD_PROMISC;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE,
+ 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+ axe_setmulti(sc);
+ AXE_SLEEPUNLOCK(sc);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->axe_if_flags & IFF_PROMISC) {
+ AXE_SLEEPLOCK(sc);
+ AXE_LOCK(sc);
+ axe_cmd(sc, AXE_CMD_RXCTL_READ,
+ 0, 0, (void *)&rxmode);
+ rxmode = le16toh(rxmode);
+ rxmode &= ~AXE_RXCMD_PROMISC;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE,
+ 0, rxmode, NULL);
+ AXE_UNLOCK(sc);
+ axe_setmulti(sc);
+ AXE_SLEEPUNLOCK(sc);
+ } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ axe_init(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ AXE_SLEEPLOCK(sc);
+ axe_stop(sc);
+ AXE_SLEEPUNLOCK(sc);
+ }
+ }
+ sc->axe_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ AXE_SLEEPLOCK(sc);
+ axe_setmulti(sc);
+ AXE_SLEEPUNLOCK(sc);
+ error = 0;
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ AXE_SLEEPLOCK(sc);
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ AXE_SLEEPUNLOCK(sc);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ return(error);
+}
+
+static void
+axe_watchdog(struct ifnet *ifp)
+{
+ struct axe_softc *sc;
+ struct axe_chain *c;
+ usbd_status stat;
+
+ sc = ifp->if_softc;
+ AXE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ device_printf(sc->axe_dev, "watchdog timeout\n");
+
+ c = &sc->axe_cdata.axe_tx_chain[0];
+ usbd_get_xfer_status(c->axe_xfer, NULL, NULL, NULL, &stat);
+ axe_txeof(c->axe_xfer, c, stat);
+
+ AXE_UNLOCK(sc);
+
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ axe_start(ifp);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+axe_stop(struct axe_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+
+ AXE_SLEEPLOCKASSERT(sc);
+ AXE_LOCK(sc);
+
+ ifp = sc->axe_ifp;
+ ifp->if_timer = 0;
+
+ untimeout(axe_tick, sc, sc->axe_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->axe_ep[AXE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->axe_dev, "abort rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->axe_dev, "close rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->axe_ep[AXE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->axe_ep[AXE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->axe_dev, "abort tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->axe_dev, "close tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->axe_ep[AXE_ENDPT_TX] = NULL;
+ }
+
+ if (sc->axe_ep[AXE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->axe_dev,
+ "abort intr pipe failed: %s\n", usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->axe_dev,
+ "close intr pipe failed: %s\n", usbd_errstr(err));
+ }
+ sc->axe_ep[AXE_ENDPT_INTR] = NULL;
+ }
+
+ axe_reset(sc);
+
+ /* Free RX resources. */
+ axe_rx_list_free(sc);
+ /* Free TX resources. */
+ axe_tx_list_free(sc);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ sc->axe_link = 0;
+ AXE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+axe_shutdown(device_t dev)
+{
+ struct axe_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ AXE_SLEEPLOCK(sc);
+ axe_stop(sc);
+ AXE_SLEEPUNLOCK(sc);
+
+ return (0);
+}
diff --git a/sys/legacy/dev/usb/if_axereg.h b/sys/legacy/dev/usb/if_axereg.h
new file mode 100644
index 0000000..4503b19
--- /dev/null
+++ b/sys/legacy/dev/usb/if_axereg.h
@@ -0,0 +1,254 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the ASIX Electronics AX88172 to ethernet controller.
+ */
+
+
+/*
+ * Vendor specific commands
+ * ASIX conveniently doesn't document the 'set NODEID' command in their
+ * datasheet (thanks a lot guys).
+ * To make handling these commands easier, I added some extra data
+ * which is decided by the axe_cmd() routine. Commands are encoded
+ * in 16 bites, with the format: LDCC. L and D are both nibbles in
+ * the high byte. L represents the data length (0 to 15) and D
+ * represents the direction (0 for vendor read, 1 for vendor write).
+ * CC is the command byte, as specified in the manual.
+ */
+
+#define AXE_CMD_DIR(x) (((x) & 0x0F00) >> 8)
+#define AXE_CMD_LEN(x) (((x) & 0xF000) >> 12)
+#define AXE_CMD_CMD(x) ((x) & 0x00FF)
+
+#define AXE_172_CMD_READ_RXTX_SRAM 0x2002
+#define AXE_182_CMD_READ_RXTX_SRAM 0x8002
+#define AXE_172_CMD_WRITE_RX_SRAM 0x0103
+#define AXE_172_CMD_WRITE_TX_SRAM 0x0104
+#define AXE_182_CMD_WRITE_RXTX_SRAM 0x8103
+#define AXE_CMD_MII_OPMODE_SW 0x0106
+#define AXE_CMD_MII_READ_REG 0x2007
+#define AXE_CMD_MII_WRITE_REG 0x2108
+#define AXE_CMD_MII_READ_OPMODE 0x1009
+#define AXE_CMD_MII_OPMODE_HW 0x010A
+#define AXE_CMD_SROM_READ 0x200B
+#define AXE_CMD_SROM_WRITE 0x010C
+#define AXE_CMD_SROM_WR_ENABLE 0x010D
+#define AXE_CMD_SROM_WR_DISABLE 0x010E
+#define AXE_CMD_RXCTL_READ 0x200F
+#define AXE_CMD_RXCTL_WRITE 0x0110
+#define AXE_CMD_READ_IPG012 0x3011
+#define AXE_172_CMD_WRITE_IPG0 0x0112
+#define AXE_172_CMD_WRITE_IPG1 0x0113
+#define AXE_172_CMD_WRITE_IPG2 0x0114
+#define AXE_178_CMD_WRITE_IPG012 0x0112
+#define AXE_CMD_READ_MCAST 0x8015
+#define AXE_CMD_WRITE_MCAST 0x8116
+#define AXE_172_CMD_READ_NODEID 0x6017
+#define AXE_172_CMD_WRITE_NODEID 0x6118
+#define AXE_178_CMD_READ_NODEID 0x6013
+#define AXE_178_CMD_WRITE_NODEID 0x6114
+#define AXE_CMD_READ_PHYID 0x2019
+#define AXE_172_CMD_READ_MEDIA 0x101A
+#define AXE_178_CMD_READ_MEDIA 0x201A
+#define AXE_CMD_WRITE_MEDIA 0x011B
+#define AXE_CMD_READ_MONITOR_MODE 0x101C
+#define AXE_CMD_WRITE_MONITOR_MODE 0x011D
+#define AXE_CMD_READ_GPIO 0x101E
+#define AXE_CMD_WRITE_GPIO 0x011F
+#define AXE_CMD_SW_RESET_REG 0x0120
+#define AXE_CMD_SW_PHY_STATUS 0x0021
+#define AXE_CMD_SW_PHY_SELECT 0x0122
+
+#define AXE_SW_RESET_CLEAR 0x00
+#define AXE_SW_RESET_RR 0x01
+#define AXE_SW_RESET_RT 0x02
+#define AXE_SW_RESET_PRTE 0x04
+#define AXE_SW_RESET_PRL 0x08
+#define AXE_SW_RESET_BZ 0x10
+#define AXE_SW_RESET_IPRL 0x20
+#define AXE_SW_RESET_IPPD 0x40
+
+/* AX88178 documentation says to always write this bit... */
+#define AXE_178_RESET_MAGIC 0x40
+
+#define AXE_178_MEDIA_GMII 0x0001
+#define AXE_MEDIA_FULL_DUPLEX 0x0002
+#define AXE_172_MEDIA_TX_ABORT_ALLOW 0x0004
+/* AX88178/88772 documentation says to always write 1 to bit 2 */
+#define AXE_178_MEDIA_MAGIC 0x0004
+/* AX88772 documentation says to always write 0 to bit 3 */
+#define AXE_178_MEDIA_ENCK 0x0008
+#define AXE_172_MEDIA_FLOW_CONTROL_EN 0x0010
+#define AXE_178_MEDIA_RXFLOW_CONTROL_EN 0x0010
+#define AXE_178_MEDIA_TXFLOW_CONTROL_EN 0x0020
+#define AXE_178_MEDIA_JUMBO_EN 0x0040
+#define AXE_178_MEDIA_LTPF_ONLY 0x0080
+#define AXE_178_MEDIA_RX_EN 0x0100
+#define AXE_178_MEDIA_100TX 0x0200
+#define AXE_178_MEDIA_SBP 0x0800
+#define AXE_178_MEDIA_SUPERMAC 0x1000
+
+#define AXE_RXCMD_PROMISC 0x0001
+#define AXE_RXCMD_ALLMULTI 0x0002
+#define AXE_172_RXCMD_UNICAST 0x0004
+#define AXE_178_RXCMD_KEEP_INVALID_CRC 0x0004
+#define AXE_RXCMD_BROADCAST 0x0008
+#define AXE_RXCMD_MULTICAST 0x0010
+#define AXE_178_RXCMD_AP 0x0020
+#define AXE_RXCMD_ENABLE 0x0080
+#define AXE_178_RXCMD_MFB_2048 0x0000 /* 2K max frame burst */
+#define AXE_178_RXCMD_MFB_4096 0x0100 /* 4K max frame burst */
+#define AXE_178_RXCMD_MFB_8192 0x0200 /* 8K max frame burst */
+#define AXE_178_RXCMD_MFB_16384 0x0300 /* 16K max frame burst*/
+
+#define AXE_NOPHY 0xE0
+#define AXE_INTPHY 0x10
+
+#define AXE_TIMEOUT 1000
+#define AXE_172_BUFSZ 1536
+#define AXE_178_MIN_BUFSZ 2048
+#define AXE_MIN_FRAMELEN 60
+#define AXE_RX_FRAMES 1
+#define AXE_TX_FRAMES 1
+
+#if AXE_178_MAX_FRAME_BURST == 0
+#define AXE_178_RXCMD_MFB AXE_178_RXCMD_MFB_2048
+#define AXE_178_MAX_BUFSZ 2048
+#elif AXE_178_MAX_FRAME_BURST == 1
+#define AXE_178_RXCMD_MFB AXE_178_RXCMD_MFB_4096
+#define AXE_178_MAX_BUFSZ 4096
+#elif AXE_178_MAX_FRAME_BURST == 2
+#define AXE_178_RXCMD_MFB AXE_178_RXCMD_MFB_8192
+#define AXE_178_MAX_BUFSZ 8192
+#else
+#define AXE_178_RXCMD_MFB AXE_178_RXCMD_MFB_16384
+#define AXE_178_MAX_BUFSZ 16384
+#endif
+
+#define AXE_RX_LIST_CNT 1
+#define AXE_TX_LIST_CNT 1
+
+struct axe_chain {
+ struct axe_softc *axe_sc;
+ usbd_xfer_handle axe_xfer;
+ char *axe_buf;
+ struct mbuf *axe_mbuf;
+ int axe_accum;
+ int axe_idx;
+};
+
+struct axe_cdata {
+ struct axe_chain axe_tx_chain[AXE_TX_LIST_CNT];
+ struct axe_chain axe_rx_chain[AXE_RX_LIST_CNT];
+ int axe_tx_prod;
+ int axe_tx_cons;
+ int axe_tx_cnt;
+ int axe_rx_prod;
+};
+
+#define AXE_CTL_READ 0x01
+#define AXE_CTL_WRITE 0x02
+
+#define AXE_CONFIG_NO 1
+#define AXE_IFACE_IDX 0
+
+/*
+ * The interrupt endpoint is currently unused
+ * by the ASIX part.
+ */
+#define AXE_ENDPT_RX 0x0
+#define AXE_ENDPT_TX 0x1
+#define AXE_ENDPT_INTR 0x2
+#define AXE_ENDPT_MAX 0x3
+
+struct axe_sframe_hdr {
+ uint16_t len;
+ uint16_t ilen;
+} __packed;
+
+struct axe_type {
+ struct usb_devno axe_dev;
+ uint32_t axe_flags;
+#define AX172 0x0000 /* AX88172 */
+#define AX178 0x0001 /* AX88178 */
+#define AX772 0x0002 /* AX88772 */
+};
+
+#define AXE_INC(x, y) (x) = (x + 1) % y
+
+struct axe_softc {
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->axe_miibus))
+#elif defined(__NetBSD__)
+#define GET_MII(sc) (&(sc)->axe_mii)
+#elif defined(__OpenBSD__)
+#define GET_MII(sc) (&(sc)->axe_mii)
+#endif
+ struct ifnet *axe_ifp;
+ device_t axe_miibus;
+ device_t axe_dev;
+ usbd_device_handle axe_udev;
+ usbd_interface_handle axe_iface;
+ u_int16_t axe_vendor;
+ u_int16_t axe_product;
+ u_int16_t axe_flags;
+ int axe_ed[AXE_ENDPT_MAX];
+ usbd_pipe_handle axe_ep[AXE_ENDPT_MAX];
+ int axe_if_flags;
+ struct axe_cdata axe_cdata;
+ struct callout_handle axe_stat_ch;
+ struct mtx axe_mtx;
+ struct sx axe_sleeplock;
+ char axe_dying;
+ int axe_link;
+ unsigned char axe_ipgs[3];
+ unsigned char axe_phyaddrs[2];
+ struct timeval axe_rx_notice;
+ struct usb_task axe_tick_task;
+ int axe_bufsz;
+ int axe_boundary;
+};
+
+#if 0
+#define AXE_LOCK(_sc) mtx_lock(&(_sc)->axe_mtx)
+#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->axe_mtx)
+#else
+#define AXE_LOCK(_sc)
+#define AXE_UNLOCK(_sc)
+#endif
+#define AXE_SLEEPLOCK(_sc) sx_xlock(&(_sc)->axe_sleeplock)
+#define AXE_SLEEPUNLOCK(_sc) sx_xunlock(&(_sc)->axe_sleeplock)
+#define AXE_SLEEPLOCKASSERT(_sc) sx_assert(&(_sc)->axe_sleeplock, SX_XLOCKED)
diff --git a/sys/legacy/dev/usb/if_cdce.c b/sys/legacy/dev/usb/if_cdce.c
new file mode 100644
index 0000000..d41a54f
--- /dev/null
+++ b/sys/legacy/dev/usb/if_cdce.c
@@ -0,0 +1,771 @@
+/* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com>
+ * Copyright (c) 2003-2005 Craig Boston
+ * Copyright (c) 2004 Daniel Hartmeier
+ * All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul, THE VOICES IN HIS HEAD OR
+ * THE 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.
+ */
+
+/*
+ * USB Communication Device Class (Ethernet Networking Control Model)
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/endian.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/usb/usbcdc.h>
+#include "usbdevs.h"
+#include <dev/usb/if_cdcereg.h>
+
+static device_probe_t cdce_match;
+static device_attach_t cdce_attach;
+static device_detach_t cdce_detach;
+static device_shutdown_t cdce_shutdown;
+
+static device_method_t cdce_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cdce_match),
+ DEVMETHOD(device_attach, cdce_attach),
+ DEVMETHOD(device_detach, cdce_detach),
+ DEVMETHOD(device_shutdown, cdce_shutdown),
+
+ { 0, 0 }
+};
+
+static driver_t cdce_driver = {
+ "cdce",
+ cdce_methods,
+ sizeof(struct cdce_softc)
+};
+
+static devclass_t cdce_devclass;
+
+DRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, usbd_driver_load, 0);
+MODULE_VERSION(cdce, 0);
+MODULE_DEPEND(cdce, usb, 1, 1, 1);
+
+static int cdce_encap(struct cdce_softc *, struct mbuf *, int);
+static void cdce_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void cdce_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void cdce_start(struct ifnet *);
+static int cdce_ioctl(struct ifnet *, u_long, caddr_t);
+static void cdce_init(void *);
+static void cdce_reset(struct cdce_softc *);
+static void cdce_stop(struct cdce_softc *);
+static void cdce_rxstart(struct ifnet *);
+static int cdce_ifmedia_upd(struct ifnet *ifp);
+static void cdce_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
+
+static const struct cdce_type cdce_devs[] = {
+ {{ USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632 }, CDCE_NO_UNION },
+ {{ USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250 }, CDCE_NO_UNION },
+ {{ USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX }, CDCE_NO_UNION },
+ {{ USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00 }, CDCE_NO_UNION },
+ {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN }, CDCE_ZAURUS | CDCE_NO_UNION },
+ {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2 }, CDCE_ZAURUS | CDCE_NO_UNION },
+ {{ USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET }, CDCE_NO_UNION },
+ {{ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501 }, CDCE_NO_UNION },
+ {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500 }, CDCE_ZAURUS },
+ {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600 }, CDCE_ZAURUS | CDCE_NO_UNION },
+ {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300 }, CDCE_ZAURUS | CDCE_NO_UNION },
+ {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700 }, CDCE_ZAURUS | CDCE_NO_UNION },
+ {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750 }, CDCE_ZAURUS | CDCE_NO_UNION },
+};
+#define cdce_lookup(v, p) ((const struct cdce_type *)usb_lookup(cdce_devs, v, p))
+
+static int
+cdce_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id == NULL)
+ return (UMATCH_NONE);
+
+ if (cdce_lookup(uaa->vendor, uaa->product) != NULL)
+ return (UMATCH_VENDOR_PRODUCT);
+
+ if (id->bInterfaceClass == UICLASS_CDC && id->bInterfaceSubClass ==
+ UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL)
+ return (UMATCH_IFACECLASS_GENERIC);
+
+ return (UMATCH_NONE);
+}
+
+static int
+cdce_attach(device_t self)
+{
+ struct cdce_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct ifnet *ifp;
+ usbd_device_handle dev = uaa->device;
+ const struct cdce_type *t;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ const usb_cdc_union_descriptor_t *ud;
+ usb_config_descriptor_t *cd;
+ int data_ifcno;
+ int i, j, numalts;
+ u_char eaddr[ETHER_ADDR_LEN];
+ const usb_cdc_ethernet_descriptor_t *ue;
+ char eaddr_str[USB_MAX_STRING_LEN];
+
+ sc->cdce_dev = self;
+ sc->cdce_udev = uaa->device;
+
+ t = cdce_lookup(uaa->vendor, uaa->product);
+ if (t)
+ sc->cdce_flags = t->cdce_flags;
+
+ if (sc->cdce_flags & CDCE_NO_UNION)
+ sc->cdce_data_iface = uaa->iface;
+ else {
+ ud = (const usb_cdc_union_descriptor_t *)usb_find_desc(sc->cdce_udev,
+ UDESC_CS_INTERFACE, UDESCSUB_CDC_UNION);
+ if (ud == NULL) {
+ device_printf(sc->cdce_dev, "no union descriptor\n");
+ return ENXIO;
+ }
+ data_ifcno = ud->bSlaveInterface[0];
+
+ for (i = 0; i < uaa->nifaces; i++) {
+ if (uaa->ifaces[i] != NULL) {
+ id = usbd_get_interface_descriptor(
+ uaa->ifaces[i]);
+ if (id != NULL && id->bInterfaceNumber ==
+ data_ifcno) {
+ sc->cdce_data_iface = uaa->ifaces[i];
+ uaa->ifaces[i] = NULL;
+ }
+ }
+ }
+ }
+
+ if (sc->cdce_data_iface == NULL) {
+ device_printf(sc->cdce_dev, "no data interface\n");
+ return ENXIO;
+ }
+
+ /*
+ * <quote>
+ * The Data Class interface of a networking device shall have a minimum
+ * of two interface settings. The first setting (the default interface
+ * setting) includes no endpoints and therefore no networking traffic is
+ * exchanged whenever the default interface setting is selected. One or
+ * more additional interface settings are used for normal operation, and
+ * therefore each includes a pair of endpoints (one IN, and one OUT) to
+ * exchange network traffic. Select an alternate interface setting to
+ * initialize the network aspects of the device and to enable the
+ * exchange of network traffic.
+ * </quote>
+ *
+ * Some devices, most notably cable modems, include interface settings
+ * that have no IN or OUT endpoint, therefore loop through the list of all
+ * available interface settings looking for one with both IN and OUT
+ * endpoints.
+ */
+ id = usbd_get_interface_descriptor(sc->cdce_data_iface);
+ cd = usbd_get_config_descriptor(sc->cdce_udev);
+ numalts = usbd_get_no_alts(cd, id->bInterfaceNumber);
+
+ for (j = 0; j < numalts; j++) {
+ if (usbd_set_interface(sc->cdce_data_iface, j)) {
+ device_printf(sc->cdce_dev,
+ "setting alternate interface failed\n");
+ return ENXIO;
+ }
+ /* Find endpoints. */
+ id = usbd_get_interface_descriptor(sc->cdce_data_iface);
+ sc->cdce_bulkin_no = sc->cdce_bulkout_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->cdce_data_iface, i);
+ if (!ed) {
+ device_printf(sc->cdce_dev,
+ "could not read endpoint descriptor\n");
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->cdce_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->cdce_bulkout_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ /* XXX: CDC spec defines an interrupt pipe, but it is not
+ * needed for simple host-to-host applications. */
+ } else {
+ device_printf(sc->cdce_dev,
+ "unexpected endpoint\n");
+ }
+ }
+ /* If we found something, try and use it... */
+ if ((sc->cdce_bulkin_no != -1) && (sc->cdce_bulkout_no != -1))
+ break;
+ }
+
+ if (sc->cdce_bulkin_no == -1) {
+ device_printf(sc->cdce_dev, "could not find data bulk in\n");
+ return ENXIO;
+ }
+ if (sc->cdce_bulkout_no == -1 ) {
+ device_printf(sc->cdce_dev, "could not find data bulk out\n");
+ return ENXIO;
+ }
+
+ mtx_init(&sc->cdce_mtx, device_get_nameunit(sc->cdce_dev), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ ifmedia_init(&sc->cdce_ifmedia, 0, cdce_ifmedia_upd, cdce_ifmedia_sts);
+ CDCE_LOCK(sc);
+
+ ue = (const usb_cdc_ethernet_descriptor_t *)usb_find_desc(dev,
+ UDESC_INTERFACE, UDESCSUB_CDC_ENF);
+ if (!ue || usbd_get_string(dev, ue->iMacAddress, eaddr_str,
+ sizeof(eaddr_str))) {
+ /* Fake MAC address */
+ device_printf(sc->cdce_dev, "faking MAC address\n");
+ eaddr[0]= 0x2a;
+ memcpy(&eaddr[1], &ticks, sizeof(u_int32_t));
+ eaddr[5] = (u_int8_t)device_get_unit(sc->cdce_dev);
+ } else {
+ int i;
+
+ memset(eaddr, 0, ETHER_ADDR_LEN);
+ for (i = 0; i < ETHER_ADDR_LEN * 2; i++) {
+ int c = eaddr_str[i];
+
+ if ('0' <= c && c <= '9')
+ c -= '0';
+ else
+ c -= 'A' - 10;
+ c &= 0xf;
+ if (c % 2 == 0)
+ c <<= 4;
+ eaddr[i / 2] |= c;
+ }
+ }
+
+ ifp = GET_IFP(sc) = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(sc->cdce_dev, "can not if_alloc()\n");
+ CDCE_UNLOCK(sc);
+ mtx_destroy(&sc->cdce_mtx);
+ return ENXIO;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "cdce", device_get_unit(sc->cdce_dev));
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT;
+ ifp->if_ioctl = cdce_ioctl;
+ ifp->if_output = ether_output;
+ ifp->if_start = cdce_start;
+ ifp->if_init = cdce_init;
+ ifp->if_baudrate = 11000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ sc->q.ifp = ifp;
+ sc->q.if_rxstart = cdce_rxstart;
+
+ /* No IFM type for 11Mbps USB, so go with 10baseT */
+ ifmedia_add(&sc->cdce_ifmedia, IFM_ETHER | IFM_10_T, 0, 0);
+ ifmedia_set(&sc->cdce_ifmedia, IFM_ETHER | IFM_10_T);
+
+ ether_ifattach(ifp, eaddr);
+ usb_register_netisr();
+
+ CDCE_UNLOCK(sc);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->cdce_udev,
+ sc->cdce_dev);
+
+ return 0;
+}
+
+static int
+cdce_detach(device_t self)
+{
+ struct cdce_softc *sc = device_get_softc(self);
+ struct ifnet *ifp;
+
+ CDCE_LOCK(sc);
+ sc->cdce_dying = 1;
+ ifp = GET_IFP(sc);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ cdce_shutdown(sc->cdce_dev);
+
+ ether_ifdetach(ifp);
+ if_free(ifp);
+ ifmedia_removeall(&sc->cdce_ifmedia);
+ CDCE_UNLOCK(sc);
+ mtx_destroy(&sc->cdce_mtx);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->cdce_udev,
+ sc->cdce_dev);
+
+ return (0);
+}
+
+static void
+cdce_start(struct ifnet *ifp)
+{
+ struct cdce_softc *sc;
+ struct mbuf *m_head = NULL;
+
+ sc = ifp->if_softc;
+ CDCE_LOCK(sc);
+
+
+ if (sc->cdce_dying ||
+ ifp->if_drv_flags & IFF_DRV_OACTIVE ||
+ !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ if (cdce_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ CDCE_UNLOCK(sc);
+
+ return;
+}
+
+static int
+cdce_encap(struct cdce_softc *sc, struct mbuf *m, int idx)
+{
+ struct ue_chain *c;
+ usbd_status err;
+ int extra = 0;
+
+ c = &sc->cdce_cdata.ue_tx_chain[idx];
+
+ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf);
+ if (sc->cdce_flags & CDCE_ZAURUS) {
+ /* Zaurus wants a 32-bit CRC appended to every frame */
+ u_int32_t crc;
+
+ crc = htole32(crc32(c->ue_buf, m->m_pkthdr.len));
+ bcopy(&crc, c->ue_buf + m->m_pkthdr.len, 4);
+ extra = 4;
+ }
+ c->ue_mbuf = m;
+
+ usbd_setup_xfer(c->ue_xfer, sc->cdce_bulkout_pipe, c, c->ue_buf,
+ m->m_pkthdr.len + extra, 0, 10000, cdce_txeof);
+ err = usbd_transfer(c->ue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ cdce_stop(sc);
+ return (EIO);
+ }
+
+ sc->cdce_cdata.ue_tx_cnt++;
+
+ return (0);
+}
+
+static void
+cdce_stop(struct cdce_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+
+ CDCE_LOCK(sc);
+
+ cdce_reset(sc);
+
+ ifp = GET_IFP(sc);
+ ifp->if_timer = 0;
+
+ if (sc->cdce_bulkin_pipe != NULL) {
+ err = usbd_abort_pipe(sc->cdce_bulkin_pipe);
+ if (err)
+ device_printf(sc->cdce_dev,
+ "abort rx pipe failed: %s\n", usbd_errstr(err));
+ err = usbd_close_pipe(sc->cdce_bulkin_pipe);
+ if (err)
+ device_printf(sc->cdce_dev,
+ "close rx pipe failed: %s\n", usbd_errstr(err));
+ sc->cdce_bulkin_pipe = NULL;
+ }
+
+ if (sc->cdce_bulkout_pipe != NULL) {
+ err = usbd_abort_pipe(sc->cdce_bulkout_pipe);
+ if (err)
+ device_printf(sc->cdce_dev,
+ "abort tx pipe failed: %s\n", usbd_errstr(err));
+ err = usbd_close_pipe(sc->cdce_bulkout_pipe);
+ if (err)
+ device_printf(sc->cdce_dev,
+ "close tx pipe failed: %s\n", usbd_errstr(err));
+ sc->cdce_bulkout_pipe = NULL;
+ }
+
+ usb_ether_rx_list_free(&sc->cdce_cdata);
+ usb_ether_tx_list_free(&sc->cdce_cdata);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ CDCE_UNLOCK(sc);
+
+ return;
+}
+
+static int
+cdce_shutdown(device_t dev)
+{
+ struct cdce_softc *sc;
+
+ sc = device_get_softc(dev);
+ cdce_stop(sc);
+
+ return (0);
+}
+
+static int
+cdce_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct cdce_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int error = 0;
+
+ if (sc->cdce_dying)
+ return (ENXIO);
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ cdce_init(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ cdce_stop(sc);
+ }
+ error = 0;
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->cdce_ifmedia, command);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ return (error);
+}
+
+static void
+cdce_reset(struct cdce_softc *sc)
+{
+ /* XXX Maybe reset the bulk pipes here? */
+ return;
+}
+
+static void
+cdce_init(void *xsc)
+{
+ struct cdce_softc *sc = xsc;
+ struct ifnet *ifp = GET_IFP(sc);
+ struct ue_chain *c;
+ usbd_status err;
+ int i;
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ return;
+
+ CDCE_LOCK(sc);
+ cdce_reset(sc);
+
+ if (usb_ether_tx_list_init(sc, &sc->cdce_cdata,
+ sc->cdce_udev) == ENOBUFS) {
+ device_printf(sc->cdce_dev, "tx list init failed\n");
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ if (usb_ether_rx_list_init(sc, &sc->cdce_cdata,
+ sc->cdce_udev) == ENOBUFS) {
+ device_printf(sc->cdce_dev, "rx list init failed\n");
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ /* Maybe set multicast / broadcast here??? */
+
+ err = usbd_open_pipe(sc->cdce_data_iface, sc->cdce_bulkin_no,
+ USBD_EXCLUSIVE_USE, &sc->cdce_bulkin_pipe);
+ if (err) {
+ device_printf(sc->cdce_dev, "open rx pipe failed: %s\n",
+ usbd_errstr(err));
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ err = usbd_open_pipe(sc->cdce_data_iface, sc->cdce_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->cdce_bulkout_pipe);
+ if (err) {
+ device_printf(sc->cdce_dev, "open tx pipe failed: %s\n",
+ usbd_errstr(err));
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ c = &sc->cdce_cdata.ue_rx_chain[i];
+ usbd_setup_xfer(c->ue_xfer, sc->cdce_bulkin_pipe, c,
+ mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, cdce_rxeof);
+ usbd_transfer(c->ue_xfer);
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ CDCE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+cdce_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ struct cdce_softc *sc = c->ue_sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ int total_len = 0;
+
+ CDCE_LOCK(sc);
+ ifp = GET_IFP(sc);
+
+ if (sc->cdce_dying || !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ CDCE_UNLOCK(sc);
+ return;
+ }
+ if (sc->cdce_rxeof_errors == 0)
+ device_printf(sc->cdce_dev, "usb error on rx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->cdce_bulkin_pipe);
+ DELAY(sc->cdce_rxeof_errors * 10000);
+ sc->cdce_rxeof_errors++;
+ goto done;
+ }
+
+ sc->cdce_rxeof_errors = 0;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ if (sc->cdce_flags & CDCE_ZAURUS)
+ total_len -= 4; /* Strip off CRC added by Zaurus */
+
+ m = c->ue_mbuf;
+
+ if (total_len < sizeof(struct ether_header)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->q;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ CDCE_UNLOCK(sc);
+
+ return;
+
+done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->cdce_bulkin_pipe, c,
+ mtod(c->ue_mbuf, char *),
+ UE_BUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+ cdce_rxeof);
+ usbd_transfer(c->ue_xfer);
+ CDCE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+cdce_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ struct cdce_softc *sc = c->ue_sc;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ CDCE_LOCK(sc);
+ ifp = GET_IFP(sc);
+
+ if (sc->cdce_dying ||
+ !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ CDCE_UNLOCK(sc);
+ return;
+ }
+ ifp->if_oerrors++;
+ device_printf(sc->cdce_dev, "usb error on tx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->cdce_bulkout_pipe);
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->ue_mbuf != NULL) {
+ c->ue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->ue_mbuf);
+ c->ue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ CDCE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+cdce_rxstart(struct ifnet *ifp)
+{
+ struct cdce_softc *sc;
+ struct ue_chain *c;
+
+ sc = ifp->if_softc;
+ CDCE_LOCK(sc);
+
+ if (sc->cdce_dying || !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ c = &sc->cdce_cdata.ue_rx_chain[sc->cdce_cdata.ue_rx_prod];
+
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL) {
+ device_printf(sc->cdce_dev, "no memory for rx list "
+ "-- packet dropped!\n");
+ ifp->if_ierrors++;
+ CDCE_UNLOCK(sc);
+ return;
+ }
+
+ usbd_setup_xfer(c->ue_xfer, sc->cdce_bulkin_pipe, c,
+ mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, cdce_rxeof);
+ usbd_transfer(c->ue_xfer);
+
+ CDCE_UNLOCK(sc);
+ return;
+}
+
+static int
+cdce_ifmedia_upd(struct ifnet *ifp)
+{
+
+ /* no-op, cdce has only 1 possible media type */
+ return 0;
+}
+
+static void
+cdce_ifmedia_sts(struct ifnet * const ifp, struct ifmediareq *req)
+{
+
+ req->ifm_status = IFM_AVALID | IFM_ACTIVE;
+ req->ifm_active = IFM_ETHER | IFM_10_T;
+}
diff --git a/sys/legacy/dev/usb/if_cdcereg.h b/sys/legacy/dev/usb/if_cdcereg.h
new file mode 100644
index 0000000..e658eab
--- /dev/null
+++ b/sys/legacy/dev/usb/if_cdcereg.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2003-2005 Craig Boston
+ * All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul, THE VOICES IN HIS HEAD OR
+ * THE 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USB_IF_CDCEREG_H_
+#define _USB_IF_CDCEREG_H_
+
+struct cdce_type {
+ struct usb_devno cdce_dev;
+ u_int16_t cdce_flags;
+#define CDCE_ZAURUS 1
+#define CDCE_NO_UNION 2
+};
+
+struct cdce_softc {
+ struct ifnet *cdce_ifp;
+#define GET_IFP(sc) ((sc)->cdce_ifp)
+ struct ifmedia cdce_ifmedia;
+
+ usbd_device_handle cdce_udev;
+ usbd_interface_handle cdce_data_iface;
+ int cdce_bulkin_no;
+ usbd_pipe_handle cdce_bulkin_pipe;
+ int cdce_bulkout_no;
+ usbd_pipe_handle cdce_bulkout_pipe;
+ char cdce_dying;
+ device_t cdce_dev;
+
+ struct ue_cdata cdce_cdata;
+ struct timeval cdce_rx_notice;
+ int cdce_rxeof_errors;
+
+ u_int16_t cdce_flags;
+
+ struct mtx cdce_mtx;
+
+ struct usb_qdat q;
+};
+
+/* We are still under Giant */
+#if 0
+#define CDCE_LOCK(_sc) mtx_lock(&(_sc)->cdce_mtx)
+#define CDCE_UNLOCK(_sc) mtx_unlock(&(_sc)->cdce_mtx)
+#else
+#define CDCE_LOCK(_sc)
+#define CDCE_UNLOCK(_sc)
+#endif
+
+#endif /* _USB_IF_CDCEREG_H_ */
diff --git a/sys/legacy/dev/usb/if_cue.c b/sys/legacy/dev/usb/if_cue.c
new file mode 100644
index 0000000..cd3a543
--- /dev/null
+++ b/sys/legacy/dev/usb/if_cue.c
@@ -0,0 +1,1074 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate
+ * adapters and others.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The
+ * RX filter uses a 512-bit multicast hash table, single perfect entry
+ * for the station address, and promiscuous mode. Unlike the ADMtek
+ * and KLSI chips, the CATC ASIC supports read and write combining
+ * mode where multiple packets can be transfered using a single bulk
+ * transaction, which helps performance a great deal.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/usb/if_cuereg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+static struct cue_type cue_devs[] = {
+ { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE },
+ { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2 },
+ { USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK },
+ /* Belkin F5U111 adapter covered by NETMATE entry */
+ { 0, 0 }
+};
+
+static device_probe_t cue_match;
+static device_attach_t cue_attach;
+static device_detach_t cue_detach;
+static device_shutdown_t cue_shutdown;
+
+static int cue_encap(struct cue_softc *, struct mbuf *, int);
+static void cue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void cue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void cue_tick(void *);
+static void cue_rxstart(struct ifnet *);
+static void cue_start(struct ifnet *);
+static int cue_ioctl(struct ifnet *, u_long, caddr_t);
+static void cue_init(void *);
+static void cue_stop(struct cue_softc *);
+static void cue_watchdog(struct ifnet *);
+
+static void cue_setmulti(struct cue_softc *);
+static uint32_t cue_mchash(const uint8_t *);
+static void cue_reset(struct cue_softc *);
+
+static int cue_csr_read_1(struct cue_softc *, int);
+static int cue_csr_write_1(struct cue_softc *, int, int);
+static int cue_csr_read_2(struct cue_softc *, int);
+#ifdef notdef
+static int cue_csr_write_2(struct cue_softc *, int, int);
+#endif
+static int cue_mem(struct cue_softc *, int, int, void *, int);
+static int cue_getmac(struct cue_softc *, void *);
+
+static device_method_t cue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cue_match),
+ DEVMETHOD(device_attach, cue_attach),
+ DEVMETHOD(device_detach, cue_detach),
+ DEVMETHOD(device_shutdown, cue_shutdown),
+
+ { 0, 0 }
+};
+
+static driver_t cue_driver = {
+ "cue",
+ cue_methods,
+ sizeof(struct cue_softc)
+};
+
+static devclass_t cue_devclass;
+
+DRIVER_MODULE(cue, uhub, cue_driver, cue_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(cue, usb, 1, 1, 1);
+MODULE_DEPEND(cue, ether, 1, 1, 1);
+
+#define CUE_SETBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x))
+
+#define CUE_CLRBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x))
+
+static int
+cue_csr_read_1(struct cue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int8_t val = 0;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(sc->cue_udev, &req, &val);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(0);
+
+ return(val);
+}
+
+static int
+cue_csr_read_2(struct cue_softc *sc, int reg)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_int16_t val = 0;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->cue_udev, &req, &val);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(0);
+
+ return(val);
+}
+
+static int
+cue_csr_write_1(struct cue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->cue_udev, &req, NULL);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+
+#ifdef notdef
+static int
+cue_csr_write_2(struct cue_softc *sc, int reg, int val)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->cue_udev, &req, NULL);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+#endif
+
+static int
+cue_mem(struct cue_softc *sc, int cmd, int addr, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ if (cmd == CUE_CMD_READSRAM)
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = cmd;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->cue_udev, &req, buf);
+
+ CUE_UNLOCK(sc);
+
+ if (err)
+ return(-1);
+
+ return(0);
+}
+
+static int
+cue_getmac(struct cue_softc *sc, void *buf)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return(0);
+
+ CUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_GET_MACADDR;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, ETHER_ADDR_LEN);
+
+ err = usbd_do_request(sc->cue_udev, &req, buf);
+
+ CUE_UNLOCK(sc);
+
+ if (err) {
+ device_printf(sc->cue_dev, "read MAC address failed\n");
+ return(-1);
+ }
+
+ return(0);
+}
+
+#define CUE_BITS 9
+
+static uint32_t
+cue_mchash(const uint8_t *addr)
+{
+ uint32_t crc;
+
+ /* Compute CRC for the address value. */
+ crc = ether_crc32_le(addr, ETHER_ADDR_LEN);
+
+ return (crc & ((1 << CUE_BITS) - 1));
+}
+
+static void
+cue_setmulti(struct cue_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ u_int32_t h = 0, i;
+
+ ifp = sc->cue_ifp;
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ for (i = 0; i < CUE_MCAST_TABLE_LEN; i++)
+ sc->cue_mctab[i] = 0xFF;
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
+ &sc->cue_mctab, CUE_MCAST_TABLE_LEN);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ for (i = 0; i < CUE_MCAST_TABLE_LEN; i++)
+ sc->cue_mctab[i] = 0;
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /*
+ * Also include the broadcast address in the filter
+ * so we can receive broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST) {
+ h = cue_mchash(ifp->if_broadcastaddr);
+ sc->cue_mctab[h >> 3] |= 1 << (h & 0x7);
+ }
+
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
+ &sc->cue_mctab, CUE_MCAST_TABLE_LEN);
+
+ return;
+}
+
+static void
+cue_reset(struct cue_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->cue_dying)
+ return;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(sc->cue_udev, &req, NULL);
+ if (err)
+ device_printf(sc->cue_dev, "reset failed\n");
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+ return;
+}
+
+/*
+ * Probe for a Pegasus chip.
+ */
+static int
+cue_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct cue_type *t;
+
+ if (!uaa->iface)
+ return(UMATCH_NONE);
+
+ t = cue_devs;
+ while(t->cue_vid) {
+ if (uaa->vendor == t->cue_vid &&
+ uaa->product == t->cue_did) {
+ return(UMATCH_VENDOR_PRODUCT);
+ }
+ t++;
+ }
+
+ return(UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+cue_attach(device_t self)
+{
+ struct cue_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ sc->cue_dev = self;
+ sc->cue_iface = uaa->iface;
+ sc->cue_udev = uaa->device;
+
+ if (usbd_set_config_no(sc->cue_udev, CUE_CONFIG_NO, 0)) {
+ device_printf(sc->cue_dev, "getting interface handle failed\n");
+ return ENXIO;
+ }
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
+ if (!ed) {
+ device_printf(sc->cue_dev, "couldn't get ep %d\n", i);
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->cue_ed[CUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->cue_ed[CUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->cue_ed[CUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ mtx_init(&sc->cue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ CUE_LOCK(sc);
+
+#ifdef notdef
+ /* Reset the adapter. */
+ cue_reset(sc);
+#endif
+ /*
+ * Get station address.
+ */
+ cue_getmac(sc, &eaddr);
+
+ ifp = sc->cue_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(sc->cue_dev, "can not if_alloc()\n");
+ CUE_UNLOCK(sc);
+ mtx_destroy(&sc->cue_mtx);
+ return ENXIO;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "cue", device_get_unit(sc->cue_dev));
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT;
+ ifp->if_ioctl = cue_ioctl;
+ ifp->if_start = cue_start;
+ ifp->if_watchdog = cue_watchdog;
+ ifp->if_init = cue_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ sc->cue_qdat.ifp = ifp;
+ sc->cue_qdat.if_rxstart = cue_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+ ether_ifattach(ifp, eaddr);
+ callout_handle_init(&sc->cue_stat_ch);
+ usb_register_netisr();
+ sc->cue_dying = 0;
+
+ CUE_UNLOCK(sc);
+ return 0;
+}
+
+static int
+cue_detach(device_t dev)
+{
+ struct cue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ CUE_LOCK(sc);
+ ifp = sc->cue_ifp;
+
+ sc->cue_dying = 1;
+ untimeout(cue_tick, sc, sc->cue_stat_ch);
+ ether_ifdetach(ifp);
+ if_free(ifp);
+
+ if (sc->cue_ep[CUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_TX]);
+ if (sc->cue_ep[CUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_RX]);
+ if (sc->cue_ep[CUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]);
+
+ CUE_UNLOCK(sc);
+ mtx_destroy(&sc->cue_mtx);
+
+ return(0);
+}
+
+static void
+cue_rxstart(struct ifnet *ifp)
+{
+ struct cue_softc *sc;
+ struct ue_chain *c;
+
+ sc = ifp->if_softc;
+ CUE_LOCK(sc);
+ c = &sc->cue_cdata.ue_rx_chain[sc->cue_cdata.ue_rx_prod];
+
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL) {
+ device_printf(sc->cue_dev, "no memory for rx list "
+ "-- packet dropped!\n");
+ ifp->if_ierrors++;
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, cue_rxeof);
+ usbd_transfer(c->ue_xfer);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void
+cue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct cue_softc *sc;
+ struct ue_chain *c;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ u_int16_t len;
+
+ c = priv;
+ sc = c->ue_sc;
+ CUE_LOCK(sc);
+ ifp = sc->cue_ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->cue_rx_notice))
+ device_printf(sc->cue_dev, "usb error on rx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->cue_ep[CUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ m = c->ue_mbuf;
+ len = *mtod(m, u_int16_t *);
+
+ /* No errors; receive the packet. */
+ total_len = len;
+
+ if (len < sizeof(struct ether_header)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ m_adj(m, sizeof(u_int16_t));
+ m->m_pkthdr.rcvif = (void *)&sc->cue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ CUE_UNLOCK(sc);
+
+ return;
+done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, cue_rxeof);
+ usbd_transfer(c->ue_xfer);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+static void
+cue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct cue_softc *sc;
+ struct ue_chain *c;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ c = priv;
+ sc = c->ue_sc;
+ CUE_LOCK(sc);
+ ifp = sc->cue_ifp;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+ device_printf(sc->cue_dev, "usb error on tx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->cue_ep[CUE_ENDPT_TX]);
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->ue_mbuf != NULL) {
+ c->ue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->ue_mbuf);
+ c->ue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+cue_tick(void *xsc)
+{
+ struct cue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = xsc;
+
+ if (sc == NULL)
+ return;
+
+ CUE_LOCK(sc);
+
+ ifp = sc->cue_ifp;
+
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL);
+
+ if (cue_csr_read_2(sc, CUE_RX_FRAMEERR))
+ ifp->if_ierrors++;
+
+ sc->cue_stat_ch = timeout(cue_tick, sc, hz);
+
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+static int
+cue_encap(struct cue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct ue_chain *c;
+ usbd_status err;
+
+ c = &sc->cue_cdata.ue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2);
+ c->ue_mbuf = m;
+
+ total_len = m->m_pkthdr.len + 2;
+
+ /* The first two bytes are the frame length */
+ c->ue_buf[0] = (u_int8_t)m->m_pkthdr.len;
+ c->ue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8);
+
+ usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_TX],
+ c, c->ue_buf, total_len, 0, 10000, cue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->ue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ cue_stop(sc);
+ return(EIO);
+ }
+
+ sc->cue_cdata.ue_tx_cnt++;
+
+ return(0);
+}
+
+static void
+cue_start(struct ifnet *ifp)
+{
+ struct cue_softc *sc;
+ struct mbuf *m_head = NULL;
+
+ sc = ifp->if_softc;
+ CUE_LOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ if (cue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+cue_init(void *xsc)
+{
+ struct cue_softc *sc = xsc;
+ struct ifnet *ifp = sc->cue_ifp;
+ struct ue_chain *c;
+ usbd_status err;
+ int i;
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ return;
+
+ CUE_LOCK(sc);
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+#ifdef foo
+ cue_reset(sc);
+#endif
+
+ /* Set MAC address */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(sc->cue_ifp)[i]);
+
+ /* Enable RX logic. */
+ cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON|CUE_ETHCTL_MCAST_ON);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC) {
+ CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ } else {
+ CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ }
+
+ /* Init TX ring. */
+ if (usb_ether_tx_list_init(sc, &sc->cue_cdata,
+ sc->cue_udev) == ENOBUFS) {
+ device_printf(sc->cue_dev, "tx list init failed\n");
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (usb_ether_rx_list_init(sc, &sc->cue_cdata,
+ sc->cue_udev) == ENOBUFS) {
+ device_printf(sc->cue_dev, "rx list init failed\n");
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Load the multicast filter. */
+ cue_setmulti(sc);
+
+ /*
+ * Set the number of RX and TX buffers that we want
+ * to reserve inside the ASIC.
+ */
+ cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES);
+ cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES);
+
+ /* Set advanced operation modes. */
+ cue_csr_write_1(sc, CUE_ADVANCED_OPMODES,
+ CUE_AOP_EMBED_RXLEN|0x01); /* 1 wait state */
+
+ /* Program the LED operation. */
+ cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->cue_dev, "open rx pipe failed: %s\n",
+ usbd_errstr(err));
+ CUE_UNLOCK(sc);
+ return;
+ }
+ err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->cue_dev, "open tx pipe failed: %s\n",
+ usbd_errstr(err));
+ CUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ c = &sc->cue_cdata.ue_rx_chain[i];
+ usbd_setup_xfer(c->ue_xfer, sc->cue_ep[CUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cue_rxeof);
+ usbd_transfer(c->ue_xfer);
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ CUE_UNLOCK(sc);
+
+ sc->cue_stat_ch = timeout(cue_tick, sc, hz);
+
+ return;
+}
+
+static int
+cue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct cue_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ CUE_LOCK(sc);
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->cue_if_flags & IFF_PROMISC)) {
+ CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ cue_setmulti(sc);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->cue_if_flags & IFF_PROMISC) {
+ CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ cue_setmulti(sc);
+ } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ cue_init(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ cue_stop(sc);
+ }
+ sc->cue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ cue_setmulti(sc);
+ error = 0;
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ CUE_UNLOCK(sc);
+
+ return(error);
+}
+
+static void
+cue_watchdog(struct ifnet *ifp)
+{
+ struct cue_softc *sc;
+ struct ue_chain *c;
+ usbd_status stat;
+
+ sc = ifp->if_softc;
+ CUE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ device_printf(sc->cue_dev, "watchdog timeout\n");
+
+ c = &sc->cue_cdata.ue_tx_chain[0];
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat);
+ cue_txeof(c->ue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ cue_start(ifp);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+cue_stop(struct cue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+
+ CUE_LOCK(sc);
+
+ ifp = sc->cue_ifp;
+ ifp->if_timer = 0;
+
+ cue_csr_write_1(sc, CUE_ETHCTL, 0);
+ cue_reset(sc);
+ untimeout(cue_tick, sc, sc->cue_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->cue_ep[CUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->cue_dev, "abort rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->cue_dev, "close rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->cue_ep[CUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->cue_ep[CUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->cue_dev, "abort tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->cue_dev, "close tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->cue_ep[CUE_ENDPT_TX] = NULL;
+ }
+
+ if (sc->cue_ep[CUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->cue_dev, "abort intr pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->cue_dev, "close intr pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->cue_ep[CUE_ENDPT_INTR] = NULL;
+ }
+
+ /* Free RX resources. */
+ usb_ether_rx_list_free(&sc->cue_cdata);
+ /* Free TX resources. */
+ usb_ether_tx_list_free(&sc->cue_cdata);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ CUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+cue_shutdown(device_t dev)
+{
+ struct cue_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ CUE_LOCK(sc);
+ cue_reset(sc);
+ cue_stop(sc);
+ CUE_UNLOCK(sc);
+
+ return (0);
+}
diff --git a/sys/legacy/dev/usb/if_cuereg.h b/sys/legacy/dev/usb/if_cuereg.h
new file mode 100644
index 0000000..4c81653
--- /dev/null
+++ b/sys/legacy/dev/usb/if_cuereg.h
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the CATC Netmate II USB to ethernet controller.
+ */
+
+
+/*
+ * Vendor specific control commands.
+ */
+#define CUE_CMD_RESET 0xF4
+#define CUE_CMD_GET_MACADDR 0xF2
+#define CUE_CMD_WRITEREG 0xFA
+#define CUE_CMD_READREG 0xFB
+#define CUE_CMD_READSRAM 0xF1
+#define CUE_CMD_WRITESRAM 0xFC
+
+/*
+ * Internal registers
+ */
+#define CUE_TX_BUFCNT 0x20
+#define CUE_RX_BUFCNT 0x21
+#define CUE_ADVANCED_OPMODES 0x22
+#define CUE_TX_BUFPKTS 0x23
+#define CUE_RX_BUFPKTS 0x24
+#define CUE_RX_MAXCHAIN 0x25
+
+#define CUE_ETHCTL 0x60
+#define CUE_ETHSTS 0x61
+#define CUE_PAR5 0x62
+#define CUE_PAR4 0x63
+#define CUE_PAR3 0x64
+#define CUE_PAR2 0x65
+#define CUE_PAR1 0x66
+#define CUE_PAR0 0x67
+
+/* Error counters, all 16 bits wide. */
+#define CUE_TX_SINGLECOLL 0x69
+#define CUE_TX_MULTICOLL 0x6B
+#define CUE_TX_EXCESSCOLL 0x6D
+#define CUE_RX_FRAMEERR 0x6F
+
+#define CUE_LEDCTL 0x81
+
+/* Advenced operating mode register */
+#define CUE_AOP_SRAMWAITS 0x03
+#define CUE_AOP_EMBED_RXLEN 0x08
+#define CUE_AOP_RXCOMBINE 0x10
+#define CUE_AOP_TXCOMBINE 0x20
+#define CUE_AOP_EVEN_PKT_READS 0x40
+#define CUE_AOP_LOOPBK 0x80
+
+/* Ethernet control register */
+#define CUE_ETHCTL_RX_ON 0x01
+#define CUE_ETHCTL_LINK_POLARITY 0x02
+#define CUE_ETHCTL_LINK_FORCE_OK 0x04
+#define CUE_ETHCTL_MCAST_ON 0x08
+#define CUE_ETHCTL_PROMISC 0x10
+
+/* Ethernet status register */
+#define CUE_ETHSTS_NO_CARRIER 0x01
+#define CUE_ETHSTS_LATECOLL 0x02
+#define CUE_ETHSTS_EXCESSCOLL 0x04
+#define CUE_ETHSTS_TXBUF_AVAIL 0x08
+#define CUE_ETHSTS_BAD_POLARITY 0x10
+#define CUE_ETHSTS_LINK_OK 0x20
+
+/* LED control register */
+#define CUE_LEDCTL_BLINK_1X 0x00
+#define CUE_LEDCTL_BLINK_2X 0x01
+#define CUE_LEDCTL_BLINK_QUARTER_ON 0x02
+#define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03
+#define CUE_LEDCTL_OFF 0x04
+#define CUE_LEDCTL_FOLLOW_LINK 0x08
+
+/*
+ * Address in ASIC's internal SRAM where the
+ * multicast hash table lives. The table is 64 bytes long,
+ * giving us a 512-bit table. We have to set the bit that
+ * corresponds to the broadcast address in order to enable
+ * reception of broadcast frames.
+ */
+#define CUE_MCAST_TABLE_ADDR 0xFA80
+#define CUE_MCAST_TABLE_LEN 64
+
+#define CUE_TIMEOUT 1000
+#define CUE_MIN_FRAMELEN 60
+#define CUE_RX_FRAMES 1
+#define CUE_TX_FRAMES 1
+
+#define CUE_CTL_READ 0x01
+#define CUE_CTL_WRITE 0x02
+
+#define CUE_CONFIG_NO 1
+
+/*
+ * The interrupt endpoint is currently unused
+ * by the KLSI part.
+ */
+#define CUE_ENDPT_RX 0x0
+#define CUE_ENDPT_TX 0x1
+#define CUE_ENDPT_INTR 0x2
+#define CUE_ENDPT_MAX 0x3
+
+struct cue_type {
+ u_int16_t cue_vid;
+ u_int16_t cue_did;
+};
+
+#define CUE_INC(x, y) (x) = (x + 1) % y
+
+struct cue_softc {
+ struct ifnet *cue_ifp;
+ device_t cue_dev;
+ usbd_device_handle cue_udev;
+ usbd_interface_handle cue_iface;
+ int cue_ed[CUE_ENDPT_MAX];
+ usbd_pipe_handle cue_ep[CUE_ENDPT_MAX];
+ u_int8_t cue_mctab[CUE_MCAST_TABLE_LEN];
+ int cue_if_flags;
+ u_int16_t cue_rxfilt;
+ struct ue_cdata cue_cdata;
+ struct callout_handle cue_stat_ch;
+ struct mtx cue_mtx;
+ char cue_dying;
+ struct timeval cue_rx_notice;
+ struct usb_qdat cue_qdat;
+};
+
+#if 0
+#define CUE_LOCK(_sc) mtx_lock(&(_sc)->cue_mtx)
+#define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->cue_mtx)
+#else
+#define CUE_LOCK(_sc)
+#define CUE_UNLOCK(_sc)
+#endif
diff --git a/sys/legacy/dev/usb/if_kue.c b/sys/legacy/dev/usb/if_kue.c
new file mode 100644
index 0000000..f68eaa8
--- /dev/null
+++ b/sys/legacy/dev/usb/if_kue.c
@@ -0,0 +1,1024 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The KLSI USB to ethernet adapter chip contains an USB serial interface,
+ * ethernet MAC and embedded microcontroller (called the QT Engine).
+ * The chip must have firmware loaded into it before it will operate.
+ * Packets are passed between the chip and host via bulk transfers.
+ * There is an interrupt endpoint mentioned in the software spec, however
+ * it's currently unused. This device is 10Mbps half-duplex only, hence
+ * there is no media selection logic. The MAC supports a 128 entry
+ * multicast filter, though the exact size of the filter can depend
+ * on the firmware. Curiously, while the software spec describes various
+ * ethernet statistics counters, my sample adapter and firmware combination
+ * claims not to support any statistics counters at all.
+ *
+ * Note that once we load the firmware in the device, we have to be
+ * careful not to load it again: if you restart your computer but
+ * leave the adapter attached to the USB controller, it may remain
+ * powered on and retain its firmware. In this case, we don't need
+ * to load the firmware a second time.
+ *
+ * Special thanks to Rob Furr for providing an ADS Technologies
+ * adapter for development and testing. No monkeys were harmed during
+ * the development of this driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/usb/if_kuereg.h>
+#include <dev/usb/kue_fw.h>
+
+MODULE_DEPEND(kue, usb, 1, 1, 1);
+MODULE_DEPEND(kue, ether, 1, 1, 1);
+
+/*
+ * Various supported device vendors/products.
+ */
+static struct kue_type kue_devs[] = {
+ { USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250 },
+ { USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460 },
+ { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450 },
+ { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT },
+ { USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BTX },
+ { USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101 },
+ { USB_VENDOR_ASANTE, USB_PRODUCT_ASANTE_EA },
+ { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T },
+ { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_DSB650C },
+ { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T },
+ { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C },
+ { USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45 },
+ { USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX1 },
+ { USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX2 },
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT },
+ { USB_VENDOR_JATON, USB_PRODUCT_JATON_EDA },
+ { USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_XX1 },
+ { USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101 },
+ { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT },
+ { USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN },
+ { USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T },
+ { USB_VENDOR_MOBILITY, USB_PRODUCT_MOBILITY_EA },
+ { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101 },
+ { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101X },
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET },
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2 },
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3 },
+ { USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA8 },
+ { USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA9 },
+ { USB_VENDOR_PORTSMITH, USB_PRODUCT_PORTSMITH_EEA },
+ { USB_VENDOR_SHARK, USB_PRODUCT_SHARK_PA },
+ { USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_U2E },
+ { USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_GPE },
+ { USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB },
+ { 0, 0 }
+};
+
+static device_probe_t kue_match;
+static device_attach_t kue_attach;
+static device_detach_t kue_detach;
+static device_shutdown_t kue_shutdown;
+static int kue_encap(struct kue_softc *, struct mbuf *, int);
+static void kue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void kue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void kue_start(struct ifnet *);
+static void kue_rxstart(struct ifnet *);
+static int kue_ioctl(struct ifnet *, u_long, caddr_t);
+static void kue_init(void *);
+static void kue_stop(struct kue_softc *);
+static void kue_watchdog(struct ifnet *);
+
+static void kue_setmulti(struct kue_softc *);
+static void kue_reset(struct kue_softc *);
+
+static usbd_status kue_do_request(usbd_device_handle,
+ usb_device_request_t *, void *);
+static usbd_status kue_ctl(struct kue_softc *, int, u_int8_t,
+ u_int16_t, char *, int);
+static usbd_status kue_setword(struct kue_softc *, u_int8_t, u_int16_t);
+static int kue_load_fw(struct kue_softc *);
+
+static device_method_t kue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, kue_match),
+ DEVMETHOD(device_attach, kue_attach),
+ DEVMETHOD(device_detach, kue_detach),
+ DEVMETHOD(device_shutdown, kue_shutdown),
+
+ { 0, 0 }
+};
+
+static driver_t kue_driver = {
+ "kue",
+ kue_methods,
+ sizeof(struct kue_softc)
+};
+
+static devclass_t kue_devclass;
+
+DRIVER_MODULE(kue, uhub, kue_driver, kue_devclass, usbd_driver_load, 0);
+
+/*
+ * We have a custom do_request function which is almost like the
+ * regular do_request function, except it has a much longer timeout.
+ * Why? Because we need to make requests over the control endpoint
+ * to download the firmware to the device, which can take longer
+ * than the default timeout.
+ */
+static usbd_status
+kue_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ xfer = usbd_alloc_xfer(dev);
+ usbd_setup_default_xfer(xfer, dev, 0, 500000, req,
+ data, UGETW(req->wLength), USBD_SHORT_XFER_OK, 0);
+ err = usbd_sync_transfer(xfer);
+ usbd_free_xfer(xfer);
+ return(err);
+}
+
+static usbd_status
+kue_setword(struct kue_softc *sc, u_int8_t breq, u_int16_t word)
+{
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->kue_dying)
+ return(USBD_NORMAL_COMPLETION);
+
+ dev = sc->kue_udev;
+
+ KUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+
+ req.bRequest = breq;
+ USETW(req.wValue, word);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = kue_do_request(dev, &req, NULL);
+
+ KUE_UNLOCK(sc);
+
+ return(err);
+}
+
+static usbd_status
+kue_ctl(struct kue_softc *sc, int rw, u_int8_t breq, u_int16_t val,
+ char *data, int len)
+{
+ usbd_device_handle dev;
+ usb_device_request_t req;
+ usbd_status err;
+
+ dev = sc->kue_udev;
+
+ if (sc->kue_dying)
+ return(USBD_NORMAL_COMPLETION);
+
+ KUE_LOCK(sc);
+
+ if (rw == KUE_CTL_WRITE)
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+
+ req.bRequest = breq;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = kue_do_request(dev, &req, data);
+
+ KUE_UNLOCK(sc);
+
+ return(err);
+}
+
+static int
+kue_load_fw(struct kue_softc *sc)
+{
+ usbd_status err;
+ usb_device_descriptor_t *dd;
+ int hwrev;
+
+ dd = &sc->kue_udev->ddesc;
+ hwrev = UGETW(dd->bcdDevice);
+
+ /*
+ * First, check if we even need to load the firmware.
+ * If the device was still attached when the system was
+ * rebooted, it may already have firmware loaded in it.
+ * If this is the case, we don't need to do it again.
+ * And in fact, if we try to load it again, we'll hang,
+ * so we have to avoid this condition if we don't want
+ * to look stupid.
+ *
+ * We can test this quickly by checking the bcdRevision
+ * code. The NIC will return a different revision code if
+ * it's probed while the firmware is still loaded and
+ * running.
+ */
+ if (hwrev == 0x0202)
+ return(0);
+
+ /* Load code segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_code_seg, sizeof(kue_code_seg));
+ if (err) {
+ device_printf(sc->kue_dev, "failed to load code segment: %s\n",
+ usbd_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Load fixup segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_fix_seg, sizeof(kue_fix_seg));
+ if (err) {
+ device_printf(sc->kue_dev, "failed to load fixup segment: %s\n",
+ usbd_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Send trigger command. */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_trig_seg, sizeof(kue_trig_seg));
+ if (err) {
+ device_printf(sc->kue_dev, "failed to load trigger segment: %s\n",
+ usbd_errstr(err));
+ return(ENXIO);
+ }
+
+ return(0);
+}
+
+static void
+kue_setmulti(struct kue_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ int i = 0;
+
+ ifp = sc->kue_ifp;
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI;
+ sc->kue_rxfilt &= ~KUE_RXFILT_MULTICAST;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
+ return;
+ }
+
+ sc->kue_rxfilt &= ~KUE_RXFILT_ALLMULTI;
+
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ /*
+ * If there are too many addresses for the
+ * internal filter, switch over to allmulti mode.
+ */
+ if (i == KUE_MCFILTCNT(sc))
+ break;
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ KUE_MCFILT(sc, i), ETHER_ADDR_LEN);
+ i++;
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ if (i == KUE_MCFILTCNT(sc))
+ sc->kue_rxfilt |= KUE_RXFILT_ALLMULTI;
+ else {
+ sc->kue_rxfilt |= KUE_RXFILT_MULTICAST;
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS,
+ i, sc->kue_mcfilters, i * ETHER_ADDR_LEN);
+ }
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
+
+ return;
+}
+
+/*
+ * Issue a SET_CONFIGURATION command to reset the MAC. This should be
+ * done after the firmware is loaded into the adapter in order to
+ * bring it into proper operation.
+ */
+static void
+kue_reset(struct kue_softc *sc)
+{
+ if (usbd_set_config_no(sc->kue_udev, KUE_CONFIG_NO, 0) ||
+ usbd_device2interface_handle(sc->kue_udev, KUE_IFACE_IDX,
+ &sc->kue_iface)) {
+ device_printf(sc->kue_dev, "getting interface handle failed\n");
+ }
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+ return;
+}
+
+/*
+ * Probe for a KLSI chip.
+ */
+static int
+kue_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct kue_type *t;
+
+ if (!uaa->iface)
+ return(UMATCH_NONE);
+
+ t = kue_devs;
+ while (t->kue_vid) {
+ if (uaa->vendor == t->kue_vid && uaa->product == t->kue_did)
+ return (UMATCH_VENDOR_PRODUCT);
+ t++;
+ }
+ return (UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do
+ * setup and ethernet/BPF attach.
+ */
+static int
+kue_attach(device_t self)
+{
+ struct kue_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct ifnet *ifp;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ sc->kue_dev = self;
+ sc->kue_iface = uaa->iface;
+ sc->kue_udev = uaa->device;
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+
+ /* Find endpoints. */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
+ if (!ed) {
+ device_printf(sc->kue_dev, "couldn't get ep %d\n", i);
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->kue_ed[KUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->kue_ed[KUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->kue_ed[KUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ mtx_init(&sc->kue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ KUE_LOCK(sc);
+
+ /* Load the firmware into the NIC. */
+ if (kue_load_fw(sc)) {
+ KUE_UNLOCK(sc);
+ mtx_destroy(&sc->kue_mtx);
+ return ENXIO;
+ }
+
+ /* Reset the adapter. */
+ kue_reset(sc);
+
+ /* Read ethernet descriptor */
+ err = kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR,
+ 0, (char *)&sc->kue_desc, sizeof(sc->kue_desc));
+
+ sc->kue_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
+ M_USBDEV, M_NOWAIT);
+
+ ifp = sc->kue_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(sc->kue_dev, "can not if_alloc()\n");
+ KUE_UNLOCK(sc);
+ mtx_destroy(&sc->kue_mtx);
+ return ENXIO;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "kue", device_get_unit(sc->kue_dev));
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT;
+ ifp->if_ioctl = kue_ioctl;
+ ifp->if_start = kue_start;
+ ifp->if_watchdog = kue_watchdog;
+ ifp->if_init = kue_init;
+ ifp->if_baudrate = 10000000;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ sc->kue_qdat.ifp = ifp;
+ sc->kue_qdat.if_rxstart = kue_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+ ether_ifattach(ifp, sc->kue_desc.kue_macaddr);
+ usb_register_netisr();
+ sc->kue_dying = 0;
+
+ KUE_UNLOCK(sc);
+
+ return 0;
+}
+
+static int
+kue_detach(device_t dev)
+{
+ struct kue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ KUE_LOCK(sc);
+ ifp = sc->kue_ifp;
+
+ sc->kue_dying = 1;
+
+ if (ifp != NULL) {
+ ether_ifdetach(ifp);
+ if_free(ifp);
+ }
+
+ if (sc->kue_ep[KUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_TX]);
+ if (sc->kue_ep[KUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_RX]);
+ if (sc->kue_ep[KUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_INTR]);
+
+ if (sc->kue_mcfilters != NULL)
+ free(sc->kue_mcfilters, M_USBDEV);
+
+ KUE_UNLOCK(sc);
+ mtx_destroy(&sc->kue_mtx);
+
+ return(0);
+}
+
+static void
+kue_rxstart(struct ifnet *ifp)
+{
+ struct kue_softc *sc;
+ struct ue_chain *c;
+
+ sc = ifp->if_softc;
+ KUE_LOCK(sc);
+ c = &sc->kue_cdata.ue_rx_chain[sc->kue_cdata.ue_rx_prod];
+
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL) {
+ device_printf(sc->kue_dev, "no memory for rx list "
+ "-- packet dropped!\n");
+ ifp->if_ierrors++;
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, kue_rxeof);
+ usbd_transfer(c->ue_xfer);
+
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void kue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct kue_softc *sc;
+ struct ue_chain *c;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ u_int16_t len;
+
+ c = priv;
+ sc = c->ue_sc;
+ KUE_LOCK(sc);
+ ifp = sc->kue_ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->kue_rx_notice))
+ device_printf(sc->kue_dev, "usb error on rx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+ m = c->ue_mbuf;
+ if (total_len <= 1)
+ goto done;
+
+ len = *mtod(m, u_int16_t *);
+ m_adj(m, sizeof(u_int16_t));
+
+ /* No errors; receive the packet. */
+ total_len = len;
+
+ if (len < sizeof(struct ether_header)) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (void *)&sc->kue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+ KUE_UNLOCK(sc);
+
+ return;
+done:
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, kue_rxeof);
+ usbd_transfer(c->ue_xfer);
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+static void
+kue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct kue_softc *sc;
+ struct ue_chain *c;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ c = priv;
+ sc = c->ue_sc;
+ KUE_LOCK(sc);
+
+ ifp = sc->kue_ifp;
+ ifp->if_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+ device_printf(sc->kue_dev, "usb error on tx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->kue_ep[KUE_ENDPT_TX]);
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->ue_mbuf != NULL) {
+ c->ue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->ue_mbuf);
+ c->ue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+static int
+kue_encap(struct kue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct ue_chain *c;
+ usbd_status err;
+
+ c = &sc->kue_cdata.ue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer, leaving two
+ * bytes at the beginning to hold the frame length.
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2);
+ c->ue_mbuf = m;
+
+ total_len = m->m_pkthdr.len + 2;
+ total_len += 64 - (total_len % 64);
+
+ /* Frame length is specified in the first 2 bytes of the buffer. */
+ c->ue_buf[0] = (u_int8_t)m->m_pkthdr.len;
+ c->ue_buf[1] = (u_int8_t)(m->m_pkthdr.len >> 8);
+
+ usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_TX],
+ c, c->ue_buf, total_len, 0, 10000, kue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->ue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ kue_stop(sc);
+ return(EIO);
+ }
+
+ sc->kue_cdata.ue_tx_cnt++;
+
+ return(0);
+}
+
+static void
+kue_start(struct ifnet *ifp)
+{
+ struct kue_softc *sc;
+ struct mbuf *m_head = NULL;
+
+ sc = ifp->if_softc;
+ KUE_LOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ if (kue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+static void
+kue_init(void *xsc)
+{
+ struct kue_softc *sc = xsc;
+ struct ifnet *ifp = sc->kue_ifp;
+ struct ue_chain *c;
+ usbd_status err;
+ int i;
+
+ KUE_LOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Set MAC address */
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC,
+ 0, IF_LLADDR(sc->kue_ifp), ETHER_ADDR_LEN);
+
+ sc->kue_rxfilt = KUE_RXFILT_UNICAST|KUE_RXFILT_BROADCAST;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ sc->kue_rxfilt |= KUE_RXFILT_PROMISC;
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->kue_rxfilt);
+
+ /* I'm not sure how to tune these. */
+#ifdef notdef
+ /*
+ * Leave this one alone for now; setting it
+ * wrong causes lockups on some machines/controllers.
+ */
+ kue_setword(sc, KUE_CMD_SET_SOFS, 1);
+#endif
+ kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64);
+
+ /* Init TX ring. */
+ if (usb_ether_tx_list_init(sc, &sc->kue_cdata,
+ sc->kue_udev) == ENOBUFS) {
+ device_printf(sc->kue_dev, "tx list init failed\n");
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (usb_ether_rx_list_init(sc, &sc->kue_cdata,
+ sc->kue_udev) == ENOBUFS) {
+ device_printf(sc->kue_dev, "rx list init failed\n");
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Load the multicast filter. */
+ kue_setmulti(sc);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->kue_dev, "open rx pipe failed: %s\n",
+ usbd_errstr(err));
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ err = usbd_open_pipe(sc->kue_iface, sc->kue_ed[KUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->kue_ep[KUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->kue_dev, "open tx pipe failed: %s\n",
+ usbd_errstr(err));
+ KUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ c = &sc->kue_cdata.ue_rx_chain[i];
+ usbd_setup_xfer(c->ue_xfer, sc->kue_ep[KUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, kue_rxeof);
+ usbd_transfer(c->ue_xfer);
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+static int
+kue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct kue_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ KUE_LOCK(sc);
+
+ switch(command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->kue_if_flags & IFF_PROMISC)) {
+ sc->kue_rxfilt |= KUE_RXFILT_PROMISC;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER,
+ sc->kue_rxfilt);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->kue_if_flags & IFF_PROMISC) {
+ sc->kue_rxfilt &= ~KUE_RXFILT_PROMISC;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER,
+ sc->kue_rxfilt);
+ } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ kue_init(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ kue_stop(sc);
+ }
+ sc->kue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ kue_setmulti(sc);
+ error = 0;
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ KUE_UNLOCK(sc);
+
+ return(error);
+}
+
+static void
+kue_watchdog(struct ifnet *ifp)
+{
+ struct kue_softc *sc;
+ struct ue_chain *c;
+ usbd_status stat;
+
+ sc = ifp->if_softc;
+ KUE_LOCK(sc);
+ ifp->if_oerrors++;
+ device_printf(sc->kue_dev, "watchdog timeout\n");
+
+ c = &sc->kue_cdata.ue_tx_chain[0];
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat);
+ kue_txeof(c->ue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ kue_start(ifp);
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+kue_stop(struct kue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+
+ KUE_LOCK(sc);
+ ifp = sc->kue_ifp;
+ ifp->if_timer = 0;
+
+ /* Stop transfers. */
+ if (sc->kue_ep[KUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->kue_dev, "abort rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->kue_dev, "close rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->kue_ep[KUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->kue_ep[KUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->kue_dev, "abort tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->kue_dev, "close tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->kue_ep[KUE_ENDPT_TX] = NULL;
+ }
+
+ if (sc->kue_ep[KUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->kue_ep[KUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->kue_dev, "abort intr pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->kue_ep[KUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->kue_dev, "close intr pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->kue_ep[KUE_ENDPT_INTR] = NULL;
+ }
+
+ /* Free RX resources. */
+ usb_ether_rx_list_free(&sc->kue_cdata);
+ /* Free TX resources. */
+ usb_ether_tx_list_free(&sc->kue_cdata);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ KUE_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+kue_shutdown(device_t dev)
+{
+ struct kue_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ kue_stop(sc);
+
+ return (0);
+}
diff --git a/sys/legacy/dev/usb/if_kuereg.h b/sys/legacy/dev/usb/if_kuereg.h
new file mode 100644
index 0000000..595eaa7
--- /dev/null
+++ b/sys/legacy/dev/usb/if_kuereg.h
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for the KLSI KL5KUSB101B USB to ethernet controller.
+ * The KLSI part is controlled via vendor control requests, the structure
+ * of which depend a bit on the firmware running on the internal
+ * microcontroller. The one exception is the 'send scan data' command,
+ * which is used to load the firmware.
+ */
+
+#define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00
+#define KUE_CMD_SET_MCAST_FILTERS 0x01
+#define KUE_CMD_SET_PKT_FILTER 0x02
+#define KUE_CMD_GET_ETHERSTATS 0x03
+#define KUE_CMD_GET_GPIO 0x04
+#define KUE_CMD_SET_GPIO 0x05
+#define KUE_CMD_SET_MAC 0x06
+#define KUE_CMD_GET_MAC 0x07
+#define KUE_CMD_SET_URB_SIZE 0x08
+#define KUE_CMD_SET_SOFS 0x09
+#define KUE_CMD_SET_EVEN_PKTS 0x0A
+#define KUE_CMD_SEND_SCAN 0xFF
+
+struct kue_ether_desc {
+ u_int8_t kue_len;
+ u_int8_t kue_rsvd0;
+ u_int8_t kue_rsvd1;
+ u_int8_t kue_macaddr[ETHER_ADDR_LEN];
+ u_int8_t kue_etherstats[4];
+ u_int8_t kue_maxseg[2];
+ u_int8_t kue_mcastfilt[2];
+ u_int8_t kue_rsvd2;
+};
+
+#define KUE_ETHERSTATS(x) \
+ (*(u_int32_t *)&(x)->kue_desc.kue_etherstats)
+#define KUE_MAXSEG(x) \
+ (*(u_int16_t *)&(x)->kue_desc.kue_maxseg)
+#define KUE_MCFILTCNT(x) \
+ ((*(u_int16_t *)&(x)->kue_desc.kue_mcastfilt) & 0x7FFF)
+#define KUE_MCFILT(x, y) \
+ (char *)&(sc->kue_mcfilters[y * ETHER_ADDR_LEN])
+
+#define KUE_STAT_TX_OK 0x00000001
+#define KUE_STAT_RX_OK 0x00000002
+#define KUE_STAT_TX_ERR 0x00000004
+#define KUE_STAT_RX_ERR 0x00000008
+#define KUE_STAT_RX_NOBUF 0x00000010
+#define KUE_STAT_TX_UCAST_BYTES 0x00000020
+#define KUE_STAT_TX_UCAST_FRAMES 0x00000040
+#define KUE_STAT_TX_MCAST_BYTES 0x00000080
+#define KUE_STAT_TX_MCAST_FRAMES 0x00000100
+#define KUE_STAT_TX_BCAST_BYTES 0x00000200
+#define KUE_STAT_TX_BCAST_FRAMES 0x00000400
+#define KUE_STAT_RX_UCAST_BYTES 0x00000800
+#define KUE_STAT_RX_UCAST_FRAMES 0x00001000
+#define KUE_STAT_RX_MCAST_BYTES 0x00002000
+#define KUE_STAT_RX_MCAST_FRAMES 0x00004000
+#define KUE_STAT_RX_BCAST_BYTES 0x00008000
+#define KUE_STAT_RX_BCAST_FRAMES 0x00010000
+#define KUE_STAT_RX_CRCERR 0x00020000
+#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000
+#define KUE_STAT_RX_ALIGNERR 0x00080000
+#define KUE_STAT_TX_SINGLECOLL 0x00100000
+#define KUE_STAT_TX_MULTICOLL 0x00200000
+#define KUE_STAT_TX_DEFERRED 0x00400000
+#define KUE_STAT_TX_MAXCOLLS 0x00800000
+#define KUE_STAT_RX_OVERRUN 0x01000000
+#define KUE_STAT_TX_UNDERRUN 0x02000000
+#define KUE_STAT_TX_SQE_ERR 0x04000000
+#define KUE_STAT_TX_CARRLOSS 0x08000000
+#define KUE_STAT_RX_LATECOLL 0x10000000
+
+#define KUE_RXFILT_PROMISC 0x0001
+#define KUE_RXFILT_ALLMULTI 0x0002
+#define KUE_RXFILT_UNICAST 0x0004
+#define KUE_RXFILT_BROADCAST 0x0008
+#define KUE_RXFILT_MULTICAST 0x0010
+
+#define KUE_TIMEOUT 1000
+#define KUE_MIN_FRAMELEN 60
+
+#define KUE_CTL_READ 0x01
+#define KUE_CTL_WRITE 0x02
+
+#define KUE_CONFIG_NO 1
+#define KUE_IFACE_IDX 0
+
+/*
+ * The interrupt endpoint is currently unused
+ * by the KLSI part.
+ */
+#define KUE_ENDPT_RX 0x0
+#define KUE_ENDPT_TX 0x1
+#define KUE_ENDPT_INTR 0x2
+#define KUE_ENDPT_MAX 0x3
+
+struct kue_type {
+ u_int16_t kue_vid;
+ u_int16_t kue_did;
+};
+
+#define KUE_INC(x, y) (x) = (x + 1) % y
+
+struct kue_softc {
+ struct ifnet *kue_ifp;
+ device_t kue_dev;
+ usbd_device_handle kue_udev;
+ usbd_interface_handle kue_iface;
+ struct kue_ether_desc kue_desc;
+ int kue_ed[KUE_ENDPT_MAX];
+ usbd_pipe_handle kue_ep[KUE_ENDPT_MAX];
+ int kue_if_flags;
+ u_int16_t kue_rxfilt;
+ u_int8_t *kue_mcfilters;
+ struct ue_cdata kue_cdata;
+ struct mtx kue_mtx;
+ char kue_dying;
+ struct timeval kue_rx_notice;
+ struct usb_qdat kue_qdat;
+};
+
+#if 0
+#define KUE_LOCK(_sc) mtx_lock(&(_sc)->kue_mtx)
+#define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->kue_mtx)
+#else
+#define KUE_LOCK(_sc)
+#define KUE_UNLOCK(_sc)
+#endif
diff --git a/sys/legacy/dev/usb/if_rue.c b/sys/legacy/dev/usb/if_rue.c
new file mode 100644
index 0000000..4449f7d
--- /dev/null
+++ b/sys/legacy/dev/usb/if_rue.c
@@ -0,0 +1,1393 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul <wpaul@ee.columbia.edu>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RealTek RTL8150 USB to fast ethernet controller driver.
+ * Datasheet is available from
+ * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net/bpf.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/if_ruereg.h>
+
+/* "device miibus" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#ifdef USB_DEBUG
+static int ruedebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue");
+SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RW,
+ &ruedebug, 0, "rue debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (ruedebug > (n)) \
+ printf x; \
+ } while (0);
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+/*
+ * Various supported device vendors/products.
+ */
+
+static struct rue_type rue_devs[] = {
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX },
+ { USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100 },
+ { 0, 0 }
+};
+
+static device_probe_t rue_match;
+static device_attach_t rue_attach;
+static device_detach_t rue_detach;
+static device_shutdown_t rue_shutdown;
+static miibus_readreg_t rue_miibus_readreg;
+static miibus_writereg_t rue_miibus_writereg;
+static miibus_statchg_t rue_miibus_statchg;
+
+static int rue_encap(struct rue_softc *, struct mbuf *, int);
+#ifdef RUE_INTR_PIPE
+static void rue_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#endif
+static void rue_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void rue_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void rue_tick(void *);
+static void rue_tick_task(void *);
+static void rue_rxstart(struct ifnet *);
+static void rue_start(struct ifnet *);
+static int rue_ioctl(struct ifnet *, u_long, caddr_t);
+static void rue_init(void *);
+static void rue_stop(struct rue_softc *);
+static void rue_watchdog(struct ifnet *);
+static int rue_ifmedia_upd(struct ifnet *);
+static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static void rue_setmulti(struct rue_softc *);
+static void rue_reset(struct rue_softc *);
+
+static int rue_read_mem(struct rue_softc *, u_int16_t, void *, u_int16_t);
+static int rue_write_mem(struct rue_softc *, u_int16_t, void *, u_int16_t);
+static int rue_csr_read_1(struct rue_softc *, int);
+static int rue_csr_write_1(struct rue_softc *, int, u_int8_t);
+static int rue_csr_read_2(struct rue_softc *, int);
+static int rue_csr_write_2(struct rue_softc *, int, u_int16_t);
+static int rue_csr_write_4(struct rue_softc *, int, u_int32_t);
+
+static device_method_t rue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rue_match),
+ DEVMETHOD(device_attach, rue_attach),
+ DEVMETHOD(device_detach, rue_detach),
+ DEVMETHOD(device_shutdown, rue_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, rue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, rue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, rue_miibus_statchg),
+
+ { 0, 0 }
+};
+
+static driver_t rue_driver = {
+ "rue",
+ rue_methods,
+ sizeof(struct rue_softc)
+};
+
+static devclass_t rue_devclass;
+
+DRIVER_MODULE(rue, uhub, rue_driver, rue_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(rue, usb, 1, 1, 1);
+MODULE_DEPEND(rue, ether, 1, 1, 1);
+MODULE_DEPEND(rue, miibus, 1, 1, 1);
+
+#define RUE_SETBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x))
+
+#define RUE_CLRBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x))
+
+#define RUE_SETBIT_2(sc, reg, x) \
+ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) | (x))
+
+#define RUE_CLRBIT_2(sc, reg, x) \
+ rue_csr_write_2(sc, reg, rue_csr_read_2(sc, reg) & ~(x))
+
+static int
+rue_read_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->rue_dying)
+ return (0);
+
+ RUE_LOCK(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->rue_udev, &req, buf);
+
+ RUE_UNLOCK(sc);
+
+ if (err) {
+ device_printf(sc->rue_dev, "control pipe read failed: %s\n",
+ usbd_errstr(err));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+rue_write_mem(struct rue_softc *sc, u_int16_t addr, void *buf, u_int16_t len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->rue_dying)
+ return (0);
+
+ RUE_LOCK(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ err = usbd_do_request(sc->rue_udev, &req, buf);
+
+ RUE_UNLOCK(sc);
+
+ if (err) {
+ device_printf(sc->rue_dev, "control pipe write failed: %s\n",
+ usbd_errstr(err));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+rue_csr_read_1(struct rue_softc *sc, int reg)
+{
+ int err;
+ u_int8_t val = 0;
+
+ err = rue_read_mem(sc, reg, &val, 1);
+
+ if (err)
+ return (0);
+
+ return (val);
+}
+
+static int
+rue_csr_read_2(struct rue_softc *sc, int reg)
+{
+ int err;
+ u_int16_t val = 0;
+ uWord w;
+
+ USETW(w, val);
+ err = rue_read_mem(sc, reg, &w, 2);
+ val = UGETW(w);
+
+ if (err)
+ return (0);
+
+ return (val);
+}
+
+static int
+rue_csr_write_1(struct rue_softc *sc, int reg, u_int8_t val)
+{
+ int err;
+
+ err = rue_write_mem(sc, reg, &val, 1);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+static int
+rue_csr_write_2(struct rue_softc *sc, int reg, u_int16_t val)
+{
+ int err;
+ uWord w;
+
+ USETW(w, val);
+ err = rue_write_mem(sc, reg, &w, 2);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+static int
+rue_csr_write_4(struct rue_softc *sc, int reg, u_int32_t val)
+{
+ int err;
+ uDWord dw;
+
+ USETDW(dw, val);
+ err = rue_write_mem(sc, reg, &dw, 4);
+
+ if (err)
+ return (-1);
+
+ return (0);
+}
+
+static int
+rue_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ int rval;
+ int ruereg;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ return (0);
+ break;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rval = rue_csr_read_1(sc, reg);
+ return (rval);
+ }
+ device_printf(sc->rue_dev, "bad phy register\n");
+ return (0);
+ }
+
+ rval = rue_csr_read_2(sc, ruereg);
+
+ return (rval);
+}
+
+static int
+rue_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ int ruereg;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ return (0);
+ break;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rue_csr_write_1(sc, reg, data);
+ return (0);
+ }
+ device_printf(sc->rue_dev, "bad phy register\n");
+ return (0);
+ }
+ rue_csr_write_2(sc, ruereg, data);
+
+ return (0);
+}
+
+static void
+rue_miibus_statchg(device_t dev)
+{
+ /*
+ * When the code below is enabled the card starts doing weird
+ * things after link going from UP to DOWN and back UP.
+ *
+ * Looks like some of register writes below messes up PHY
+ * interface.
+ *
+ * No visible regressions were found after commenting this code
+ * out, so that disable it for good.
+ */
+#if 0
+ struct rue_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ int bmcr;
+
+ RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+
+ bmcr = rue_csr_read_2(sc, RUE_BMCR);
+
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ bmcr |= RUE_BMCR_SPD_SET;
+ else
+ bmcr &= ~RUE_BMCR_SPD_SET;
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ bmcr |= RUE_BMCR_DUPLEX;
+ else
+ bmcr &= ~RUE_BMCR_DUPLEX;
+
+ rue_csr_write_2(sc, RUE_BMCR, bmcr);
+
+ RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+#endif
+}
+
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+
+static void
+rue_setmulti(struct rue_softc *sc)
+{
+ struct ifnet *ifp;
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+ u_int32_t rxcfg;
+ int mcnt = 0;
+
+ ifp = sc->rue_ifp;
+
+ rxcfg = rue_csr_read_2(sc, RUE_RCR);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP);
+ rxcfg &= ~RUE_RCR_AM;
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF);
+ rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ rue_csr_write_4(sc, RUE_MAR0, 0);
+ rue_csr_write_4(sc, RUE_MAR4, 0);
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ mcnt++;
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ if (mcnt)
+ rxcfg |= RUE_RCR_AM;
+ else
+ rxcfg &= ~RUE_RCR_AM;
+
+ rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP);
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, hashes[0]);
+ rue_csr_write_4(sc, RUE_MAR4, hashes[1]);
+}
+
+static void
+rue_reset(struct rue_softc *sc)
+{
+ int i;
+
+ rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST);
+
+ for (i = 0; i < RUE_TIMEOUT; i++) {
+ DELAY(500);
+ if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST))
+ break;
+ }
+ if (i == RUE_TIMEOUT)
+ device_printf(sc->rue_dev, "reset never completed!\n");
+
+ DELAY(10000);
+}
+
+/*
+ * Probe for a RTL8150 chip.
+ */
+
+static int
+rue_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct rue_type *t;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ t = rue_devs;
+ while (t->rue_vid) {
+ if (uaa->vendor == t->rue_vid &&
+ uaa->product == t->rue_did) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ t++;
+ }
+
+ return (UMATCH_NONE);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+
+static int
+rue_attach(device_t self)
+{
+ struct rue_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct ifnet *ifp;
+ usbd_interface_handle iface;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+ struct rue_type *t;
+
+ sc->rue_dev = self;
+ sc->rue_udev = uaa->device;
+
+ if (usbd_set_config_no(sc->rue_udev, RUE_CONFIG_NO, 0)) {
+ device_printf(sc->rue_dev, "getting interface handle failed\n");
+ goto error;
+ }
+
+ usb_init_task(&sc->rue_tick_task, rue_tick_task, sc);
+
+ err = usbd_device2interface_handle(uaa->device, RUE_IFACE_IDX, &iface);
+ if (err) {
+ device_printf(sc->rue_dev, "getting interface handle failed\n");
+ goto error;
+ }
+
+ sc->rue_iface = iface;
+
+ t = rue_devs;
+ while (t->rue_vid) {
+ if (uaa->vendor == t->rue_vid &&
+ uaa->product == t->rue_did) {
+ sc->rue_info = t;
+ break;
+ }
+ t++;
+ }
+
+ id = usbd_get_interface_descriptor(sc->rue_iface);
+
+ /* Find endpoints */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ device_printf(sc->rue_dev, "couldn't get ep %d\n", i);
+ goto error;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->rue_ed[RUE_ENDPT_RX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->rue_ed[RUE_ENDPT_TX] = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->rue_ed[RUE_ENDPT_INTR] = ed->bEndpointAddress;
+ }
+ }
+
+ mtx_init(&sc->rue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ RUE_LOCK(sc);
+
+ /* Reset the adapter */
+ rue_reset(sc);
+
+ /* Get station address from the EEPROM */
+ err = rue_read_mem(sc, RUE_EEPROM_IDR0,
+ (caddr_t)&eaddr, ETHER_ADDR_LEN);
+ if (err) {
+ device_printf(sc->rue_dev, "couldn't get station address\n");
+ goto error1;
+ }
+
+ ifp = sc->rue_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(sc->rue_dev, "can not if_alloc()\n");
+ goto error1;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "rue", device_get_unit(sc->rue_dev));
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT;
+ ifp->if_ioctl = rue_ioctl;
+ ifp->if_start = rue_start;
+ ifp->if_watchdog = rue_watchdog;
+ ifp->if_init = rue_init;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ /* MII setup */
+ if (mii_phy_probe(self, &sc->rue_miibus,
+ rue_ifmedia_upd, rue_ifmedia_sts)) {
+ device_printf(sc->rue_dev, "MII without any PHY!\n");
+ goto error2;
+ }
+
+ sc->rue_qdat.ifp = ifp;
+ sc->rue_qdat.if_rxstart = rue_rxstart;
+
+ /* Call MI attach routine */
+ ether_ifattach(ifp, eaddr);
+ callout_handle_init(&sc->rue_stat_ch);
+ usb_register_netisr();
+ sc->rue_dying = 0;
+
+ RUE_UNLOCK(sc);
+ return 0;
+
+ error2:
+ if_free(ifp);
+ error1:
+ RUE_UNLOCK(sc);
+ mtx_destroy(&sc->rue_mtx);
+ error:
+ return ENXIO;
+}
+
+static int
+rue_detach(device_t dev)
+{
+ struct rue_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ RUE_LOCK(sc);
+ ifp = sc->rue_ifp;
+
+ sc->rue_dying = 1;
+ untimeout(rue_tick, sc, sc->rue_stat_ch);
+ usb_rem_task(sc->rue_udev, &sc->rue_tick_task);
+ ether_ifdetach(ifp);
+ if_free(ifp);
+
+ if (sc->rue_ep[RUE_ENDPT_TX] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (sc->rue_ep[RUE_ENDPT_RX] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+#ifdef RUE_INTR_PIPE
+ if (sc->rue_ep[RUE_ENDPT_INTR] != NULL)
+ usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+#endif
+
+ RUE_UNLOCK(sc);
+ mtx_destroy(&sc->rue_mtx);
+
+ return (0);
+}
+
+#ifdef RUE_INTR_PIPE
+static void
+rue_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rue_softc *sc = priv;
+ struct ifnet *ifp;
+ struct rue_intrpkt *p;
+
+ RUE_LOCK(sc);
+ ifp = sc->rue_ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ device_printf(sc->rue_dev, "usb error on intr: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_INTR]);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&p, NULL, NULL);
+
+ ifp->if_ierrors += p->rue_rxlost_cnt;
+ ifp->if_ierrors += p->rue_crcerr_cnt;
+ ifp->if_collisions += p->rue_col_cnt;
+
+ RUE_UNLOCK(sc);
+}
+#endif
+
+static void
+rue_rxstart(struct ifnet *ifp)
+{
+ struct rue_softc *sc;
+ struct ue_chain *c;
+
+ sc = ifp->if_softc;
+ RUE_LOCK(sc);
+ c = &sc->rue_cdata.ue_rx_chain[sc->rue_cdata.ue_rx_prod];
+
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL) {
+ printf("%s: no memory for rx list "
+ "-- packet dropped!\n", device_get_nameunit(sc->rue_dev));
+ ifp->if_ierrors++;
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ, USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(c->ue_xfer);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+
+static void
+rue_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ struct rue_softc *sc = c->ue_sc;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ int total_len = 0;
+ struct rue_rxpkt r;
+
+ if (sc->rue_dying)
+ return;
+ RUE_LOCK(sc);
+ ifp = sc->rue_ifp;
+
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ if (usbd_ratecheck(&sc->rue_rx_notice))
+ device_printf(sc->rue_dev, "usb error on rx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_RX]);
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ if (total_len <= ETHER_CRC_LEN) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ m = c->ue_mbuf;
+ bcopy(mtod(m, char *) + total_len - 4, (char *)&r, sizeof (r));
+
+ /* Check recieve packet was valid or not */
+ if ((r.rue_rxstat & RUE_RXSTAT_VALID) == 0) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ /* No errors; receive the packet. */
+ total_len -= ETHER_CRC_LEN;
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = (void *)&sc->rue_qdat;
+ m->m_pkthdr.len = m->m_len = total_len;
+
+ /* Put the packet on the special USB input queue. */
+ usb_ether_input(m);
+
+ RUE_UNLOCK(sc);
+ return;
+
+ done:
+ /* Setup new transfer. */
+ usbd_setup_xfer(xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(xfer);
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+static void
+rue_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ struct rue_softc *sc = c->ue_sc;
+ struct ifnet *ifp;
+ usbd_status err;
+
+ RUE_LOCK(sc);
+
+ ifp = sc->rue_ifp;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+ device_printf(sc->rue_dev, "usb error on tx: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->rue_ep[RUE_ENDPT_TX]);
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ ifp->if_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &err);
+
+ if (c->ue_mbuf != NULL) {
+ c->ue_mbuf->m_pkthdr.rcvif = ifp;
+ usb_tx_done(c->ue_mbuf);
+ c->ue_mbuf = NULL;
+ }
+
+ if (err)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
+ RUE_UNLOCK(sc);
+}
+
+static void
+rue_tick(void *xsc)
+{
+ struct rue_softc *sc = xsc;
+
+ if (sc == NULL)
+ return;
+ if (sc->rue_dying)
+ return;
+
+ /* Perform periodic stuff in process context */
+ usb_add_task(sc->rue_udev, &sc->rue_tick_task, USB_TASKQ_DRIVER);
+}
+
+static void
+rue_tick_task(void *xsc)
+{
+ struct rue_softc *sc = xsc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+
+ if (sc == NULL)
+ return;
+
+ RUE_LOCK(sc);
+
+ ifp = sc->rue_ifp;
+ mii = GET_MII(sc);
+ if (mii == NULL) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ mii_tick(mii);
+ if (!sc->rue_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->rue_link++;
+ if (ifp->if_snd.ifq_head != NULL)
+ rue_start(ifp);
+ }
+
+ sc->rue_stat_ch = timeout(rue_tick, sc, hz);
+
+ RUE_UNLOCK(sc);
+}
+
+static int
+rue_encap(struct rue_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct ue_chain *c;
+ usbd_status err;
+
+ c = &sc->rue_cdata.ue_tx_chain[idx];
+
+ /*
+ * Copy the mbuf data into a contiguous buffer
+ */
+ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf);
+ c->ue_mbuf = m;
+
+ total_len = m->m_pkthdr.len;
+
+ /*
+ * This is an undocumented behavior.
+ * RTL8150 chip doesn't send frame length smaller than
+ * RUE_MIN_FRAMELEN (60) byte packet.
+ */
+ if (total_len < RUE_MIN_FRAMELEN)
+ total_len = RUE_MIN_FRAMELEN;
+
+ usbd_setup_xfer(c->ue_xfer, sc->rue_ep[RUE_ENDPT_TX],
+ c, c->ue_buf, total_len, USBD_FORCE_SHORT_XFER,
+ 10000, rue_txeof);
+
+ /* Transmit */
+ err = usbd_transfer(c->ue_xfer);
+ if (err != USBD_IN_PROGRESS) {
+ rue_stop(sc);
+ return (EIO);
+ }
+
+ sc->rue_cdata.ue_tx_cnt++;
+
+ return (0);
+}
+
+static void
+rue_start(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mbuf *m_head = NULL;
+
+ RUE_LOCK(sc);
+
+ if (!sc->rue_link) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ if (rue_encap(sc, m_head, 0)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m_head);
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+
+ RUE_UNLOCK(sc);
+}
+
+static void
+rue_init(void *xsc)
+{
+ struct rue_softc *sc = xsc;
+ struct ifnet *ifp = sc->rue_ifp;
+ struct mii_data *mii = GET_MII(sc);
+ struct ue_chain *c;
+ usbd_status err;
+ int i;
+ int rxcfg;
+
+ RUE_LOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ rue_reset(sc);
+
+ /* Set MAC address */
+ rue_write_mem(sc, RUE_IDR0, IF_LLADDR(sc->rue_ifp),
+ ETHER_ADDR_LEN);
+
+ /* Init TX ring. */
+ if (usb_ether_tx_list_init(sc, &sc->rue_cdata,
+ sc->rue_udev) == ENOBUFS) {
+ device_printf(sc->rue_dev, "tx list init failed\n");
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+ /* Init RX ring. */
+ if (usb_ether_rx_list_init(sc, &sc->rue_cdata,
+ sc->rue_udev) == ENOBUFS) {
+ device_printf(sc->rue_dev, "rx list init failed\n");
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef RUE_INTR_PIPE
+ sc->rue_cdata.ue_ibuf = malloc(RUE_INTR_PKTLEN, M_USBDEV, M_NOWAIT);
+#endif
+
+ /*
+ * Set the initial TX and RX configuration.
+ */
+ rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG);
+
+ rxcfg = RUE_RCR_CONFIG;
+
+ /* Set capture broadcast bit to capture broadcast frames. */
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxcfg |= RUE_RCR_AB;
+ else
+ rxcfg &= ~RUE_RCR_AB;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxcfg |= RUE_RCR_AAP;
+ else
+ rxcfg &= ~RUE_RCR_AAP;
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+
+ /* Load the multicast filter. */
+ rue_setmulti(sc);
+
+ /* Enable RX and TX */
+ rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN));
+
+ mii_mediachg(mii);
+
+ /* Open RX and TX pipes. */
+ err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_RX],
+ USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->rue_dev, "open rx pipe failed: %s\n",
+ usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+ err = usbd_open_pipe(sc->rue_iface, sc->rue_ed[RUE_ENDPT_TX],
+ USBD_EXCLUSIVE_USE, &sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->rue_dev, "open tx pipe failed: %s\n",
+ usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+
+#ifdef RUE_INTR_PIPE
+ err = usbd_open_pipe_intr(sc->rue_iface, sc->rue_ed[RUE_ENDPT_INTR],
+ USBD_SHORT_XFER_OK,
+ &sc->rue_ep[RUE_ENDPT_INTR], sc,
+ sc->rue_cdata.ue_ibuf, RUE_INTR_PKTLEN,
+ rue_intr, RUE_INTR_INTERVAL);
+ if (err) {
+ device_printf(sc->rue_dev, "open intr pipe failed: %s\n",
+ usbd_errstr(err));
+ RUE_UNLOCK(sc);
+ return;
+ }
+#endif
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ c = &sc->rue_cdata.ue_rx_chain[i];
+ usbd_setup_xfer(c->ue_xfer, sc->rue_ep[RUE_ENDPT_RX],
+ c, mtod(c->ue_mbuf, char *), UE_BUFSZ,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rue_rxeof);
+ usbd_transfer(c->ue_xfer);
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ sc->rue_stat_ch = timeout(rue_tick, sc, hz);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Set media options.
+ */
+
+static int
+rue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ sc->rue_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ LIST_FOREACH (miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+
+static void
+rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static int
+rue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ int error = 0;
+
+ RUE_LOCK(sc);
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ ifp->if_flags & IFF_PROMISC &&
+ !(sc->rue_if_flags & IFF_PROMISC)) {
+ RUE_SETBIT_2(sc, RUE_RCR,
+ (RUE_RCR_AAM | RUE_RCR_AAP));
+ rue_setmulti(sc);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC) &&
+ sc->rue_if_flags & IFF_PROMISC) {
+ RUE_CLRBIT_2(sc, RUE_RCR,
+ (RUE_RCR_AAM | RUE_RCR_AAP));
+ rue_setmulti(sc);
+ } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ rue_init(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ rue_stop(sc);
+ }
+ sc->rue_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ rue_setmulti(sc);
+ error = 0;
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ RUE_UNLOCK(sc);
+
+ return (error);
+}
+
+static void
+rue_watchdog(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct ue_chain *c;
+ usbd_status stat;
+
+ RUE_LOCK(sc);
+
+ ifp->if_oerrors++;
+ device_printf(sc->rue_dev, "watchdog timeout\n");
+
+ c = &sc->rue_cdata.ue_tx_chain[0];
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat);
+ rue_txeof(c->ue_xfer, c, stat);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ rue_start(ifp);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+
+static void
+rue_stop(struct rue_softc *sc)
+{
+ usbd_status err;
+ struct ifnet *ifp;
+
+ RUE_LOCK(sc);
+
+ ifp = sc->rue_ifp;
+ ifp->if_timer = 0;
+
+ rue_csr_write_1(sc, RUE_CR, 0x00);
+ rue_reset(sc);
+
+ untimeout(rue_tick, sc, sc->rue_stat_ch);
+
+ /* Stop transfers. */
+ if (sc->rue_ep[RUE_ENDPT_RX] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->rue_dev, "abort rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_RX]);
+ if (err) {
+ device_printf(sc->rue_dev, "close rx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_RX] = NULL;
+ }
+
+ if (sc->rue_ep[RUE_ENDPT_TX] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->rue_dev, "abort tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_TX]);
+ if (err) {
+ device_printf(sc->rue_dev, "close tx pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_TX] = NULL;
+ }
+
+#ifdef RUE_INTR_PIPE
+ if (sc->rue_ep[RUE_ENDPT_INTR] != NULL) {
+ err = usbd_abort_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->rue_dev, "abort intr pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ err = usbd_close_pipe(sc->rue_ep[RUE_ENDPT_INTR]);
+ if (err) {
+ device_printf(sc->rue_dev, "close intr pipe failed: %s\n",
+ usbd_errstr(err));
+ }
+ sc->rue_ep[RUE_ENDPT_INTR] = NULL;
+ }
+#endif
+
+ /* Free RX resources. */
+ usb_ether_rx_list_free(&sc->rue_cdata);
+ /* Free TX resources. */
+ usb_ether_tx_list_free(&sc->rue_cdata);
+
+#ifdef RUE_INTR_PIPE
+ free(sc->rue_cdata.ue_ibuf, M_USBDEV);
+ sc->rue_cdata.ue_ibuf = NULL;
+#endif
+
+ sc->rue_link = 0;
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ RUE_UNLOCK(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+
+static int
+rue_shutdown(device_t dev)
+{
+ struct rue_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ sc->rue_dying++;
+ RUE_LOCK(sc);
+ rue_reset(sc);
+ rue_stop(sc);
+ RUE_UNLOCK(sc);
+
+ return (0);
+}
diff --git a/sys/legacy/dev/usb/if_ruereg.h b/sys/legacy/dev/usb/if_ruereg.h
new file mode 100644
index 0000000..da6470f
--- /dev/null
+++ b/sys/legacy/dev/usb/if_ruereg.h
@@ -0,0 +1,226 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IF_RUEREG_H_
+#define _IF_RUEREG_H_
+
+#define RUE_INTR_PIPE 1 /* Use INTR PIPE */
+
+#define RUE_CONFIG_NO 1
+#define RUE_IFACE_IDX 0
+
+#define RUE_ENDPT_RX 0x0
+#define RUE_ENDPT_TX 0x1
+#define RUE_ENDPT_INTR 0x2
+#define RUE_ENDPT_MAX 0x3
+
+#define RUE_INTR_PKTLEN 0x8
+
+#define RUE_TIMEOUT 1000
+#define ETHER_ALIGN 2
+#define RUE_MIN_FRAMELEN 60
+#define RUE_INTR_INTERVAL 100 /* ms */
+
+/*
+ * Registers
+ */
+
+#define RUE_IDR0 0x0120
+#define RUE_IDR1 0x0121
+#define RUE_IDR2 0x0122
+#define RUE_IDR3 0x0123
+#define RUE_IDR4 0x0124
+#define RUE_IDR5 0x0125
+
+#define RUE_MAR0 0x0126
+#define RUE_MAR1 0x0127
+#define RUE_MAR2 0x0128
+#define RUE_MAR3 0x0129
+#define RUE_MAR4 0x012A
+#define RUE_MAR5 0x012B
+#define RUE_MAR6 0x012C
+#define RUE_MAR7 0x012D
+
+#define RUE_CR 0x012E /* B, R/W */
+#define RUE_CR_SOFT_RST 0x10
+#define RUE_CR_RE 0x08
+#define RUE_CR_TE 0x04
+#define RUE_CR_EP3CLREN 0x02
+
+#define RUE_TCR 0x012F /* B, R/W */
+#define RUE_TCR_TXRR1 0x80
+#define RUE_TCR_TXRR0 0x40
+#define RUE_TCR_IFG1 0x10
+#define RUE_TCR_IFG0 0x08
+#define RUE_TCR_NOCRC 0x01
+#define RUE_TCR_CONFIG (RUE_TCR_TXRR1|RUE_TCR_TXRR0|RUE_TCR_IFG1|RUE_TCR_IFG0)
+
+#define RUE_RCR 0x0130 /* W, R/W */
+#define RUE_RCR_TAIL 0x80
+#define RUE_RCR_AER 0x40
+#define RUE_RCR_AR 0x20
+#define RUE_RCR_AM 0x10
+#define RUE_RCR_AB 0x08
+#define RUE_RCR_AD 0x04
+#define RUE_RCR_AAM 0x02
+#define RUE_RCR_AAP 0x01
+#define RUE_RCR_CONFIG (RUE_RCR_TAIL|RUE_RCR_AD)
+
+#define RUE_TSR 0x0132
+#define RUE_RSR 0x0133
+#define RUE_CON0 0x0135
+#define RUE_CON1 0x0136
+#define RUE_MSR 0x0137
+#define RUE_PHYADD 0x0138
+#define RUE_PHYDAT 0x0139
+
+#define RUE_PHYCNT 0x013B /* B, R/W */
+#define RUE_PHYCNT_PHYOWN 0x40
+#define RUE_PHYCNT_RWCR 0x20
+
+#define RUE_GPPC 0x013D
+#define RUE_WAKECNT 0x013E
+
+#define RUE_BMCR 0x0140
+#define RUE_BMCR_SPD_SET 0x2000
+#define RUE_BMCR_DUPLEX 0x0100
+
+#define RUE_BMSR 0x0142
+
+#define RUE_ANAR 0x0144 /* W, R/W */
+#define RUE_ANAR_PAUSE 0x0400
+
+#define RUE_ANLP 0x0146 /* W, R/O */
+#define RUE_ANLP_PAUSE 0x0400
+
+#define RUE_AER 0x0148
+
+#define RUE_NWAYT 0x014A
+#define RUE_CSCR 0x014C
+
+#define RUE_CRC0 0x014E
+#define RUE_CRC1 0x0150
+#define RUE_CRC2 0x0152
+#define RUE_CRC3 0x0154
+#define RUE_CRC4 0x0156
+
+#define RUE_BYTEMASK0 0x0158
+#define RUE_BYTEMASK1 0x0160
+#define RUE_BYTEMASK2 0x0168
+#define RUE_BYTEMASK3 0x0170
+#define RUE_BYTEMASK4 0x0178
+
+#define RUE_PHY1 0x0180
+#define RUE_PHY2 0x0184
+
+#define RUE_TW1 0x0186
+
+#define RUE_REG_MIN 0x0120
+#define RUE_REG_MAX 0x0189
+
+/*
+ * EEPROM address declarations
+ */
+
+#define RUE_EEPROM_BASE 0x1200
+
+#define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02)
+#define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03)
+
+#define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17)
+
+struct rue_intrpkt {
+ u_int8_t rue_tsr;
+ u_int8_t rue_rsr;
+ u_int8_t rue_gep_msr;
+ u_int8_t rue_waksr;
+ u_int8_t rue_txok_cnt;
+ u_int8_t rue_rxlost_cnt;
+ u_int8_t rue_crcerr_cnt;
+ u_int8_t rue_col_cnt;
+};
+
+struct rue_rxpkt {
+ u_int16_t rue_pktlen : 12;
+ u_int16_t rue_rxstat : 4;
+};
+
+#define RUE_RXSTAT_VALID 0x01
+#define RUE_RXSTAT_RUNT 0x02
+#define RUE_RXSTAT_PMATCH 0x04
+#define RUE_RXSTAT_MCAST 0x08
+
+#define RUE_RXSTAT_MASK RUE_RXSTAT_VALID
+
+struct rue_type {
+ u_int16_t rue_vid;
+ u_int16_t rue_did;
+};
+
+struct rue_softc {
+ struct ifnet *rue_ifp;
+ device_t rue_dev;
+ device_t rue_miibus;
+ usbd_device_handle rue_udev;
+ usbd_interface_handle rue_iface;
+ struct rue_type *rue_info;
+ int rue_ed[RUE_ENDPT_MAX];
+ usbd_pipe_handle rue_ep[RUE_ENDPT_MAX];
+ int rue_unit;
+ u_int8_t rue_link;
+ int rue_if_flags;
+ struct ue_cdata rue_cdata;
+ struct callout_handle rue_stat_ch;
+ struct mtx rue_mtx;
+ char rue_dying;
+ struct timeval rue_rx_notice;
+ struct usb_qdat rue_qdat;
+ struct usb_task rue_tick_task;
+};
+
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->rue_miibus))
+#elif defined(__NetBSD__)
+#define GET_MII(sc) (&(sc)->rue_mii)
+#elif defined(__OpenBSD__)
+#define GET_MII(sc) (&(sc)->rue_mii)
+#endif
+
+#if 0
+#define RUE_LOCK(_sc) mtx_lock(&(_sc)->rue_mtx)
+#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->rue_mtx)
+#else
+#define RUE_LOCK(_sc)
+#define RUE_UNLOCK(_sc)
+#endif
+
+#endif /* _IF_RUEREG_H_ */
diff --git a/sys/legacy/dev/usb/if_rum.c b/sys/legacy/dev/usb/if_rum.c
new file mode 100644
index 0000000..c0c7cb6
--- /dev/null
+++ b/sys/legacy/dev/usb/if_rum.c
@@ -0,0 +1,2560 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005-2007 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2501USB/RT2601USB chipset driver
+ * http://www.ralinktech.com.tw/
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_regdomain.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/if_rumreg.h>
+#include <dev/usb/if_rumvar.h>
+#include <dev/usb/rt2573_ucode.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) do { if (rumdebug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (rumdebug >= (n)) printf x; } while (0)
+int rumdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum");
+SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RW, &rumdebug, 0,
+ "rum debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+/* various supported device vendors/products */
+static const struct usb_devno rum_devs[] = {
+ { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM },
+ { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2 },
+ { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3 },
+ { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4 },
+ { USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700 },
+ { USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO },
+ { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1 },
+ { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2 },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3 },
+ { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC },
+ { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR },
+ { USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2 },
+ { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL },
+ { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX },
+ { USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F },
+ { USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573 },
+ { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1 },
+ { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340 },
+ { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111 },
+ { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110 },
+ { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS },
+ { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS },
+ { USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573 },
+ { USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573 },
+ { USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB },
+ { USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP },
+ { USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HG },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1 },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2 },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3 },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4 },
+ { USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573 },
+ { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP },
+ { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2 },
+ { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM },
+ { USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573 },
+ { USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2 },
+ { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573 },
+ { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2 },
+ { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671 },
+ { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2 },
+ { USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172 },
+ { USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573 },
+ { USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573 }
+};
+
+MODULE_DEPEND(rum, wlan, 1, 1, 1);
+MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1);
+MODULE_DEPEND(rum, usb, 1, 1, 1);
+
+static struct ieee80211vap *rum_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rum_vap_delete(struct ieee80211vap *);
+static int rum_alloc_tx_list(struct rum_softc *);
+static void rum_free_tx_list(struct rum_softc *);
+static int rum_alloc_rx_list(struct rum_softc *);
+static void rum_free_rx_list(struct rum_softc *);
+static void rum_task(void *);
+static void rum_scantask(void *);
+static int rum_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static void rum_txeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+static void rum_rxeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+static void rum_setup_tx_desc(struct rum_softc *,
+ struct rum_tx_desc *, uint32_t, uint16_t, int,
+ int);
+static int rum_tx_mgt(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int rum_tx_raw(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *,
+ const struct ieee80211_bpf_params *);
+static int rum_tx_data(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static void rum_start(struct ifnet *);
+static void rum_watchdog(void *);
+static int rum_ioctl(struct ifnet *, u_long, caddr_t);
+static void rum_eeprom_read(struct rum_softc *, uint16_t, void *,
+ int);
+static uint32_t rum_read(struct rum_softc *, uint16_t);
+static void rum_read_multi(struct rum_softc *, uint16_t, void *,
+ int);
+static void rum_write(struct rum_softc *, uint16_t, uint32_t);
+static void rum_write_multi(struct rum_softc *, uint16_t, void *,
+ size_t);
+static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t);
+static uint8_t rum_bbp_read(struct rum_softc *, uint8_t);
+static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t);
+static void rum_select_antenna(struct rum_softc *);
+static void rum_enable_mrr(struct rum_softc *);
+static void rum_set_txpreamble(struct rum_softc *);
+static void rum_set_basicrates(struct rum_softc *);
+static void rum_select_band(struct rum_softc *,
+ struct ieee80211_channel *);
+static void rum_set_chan(struct rum_softc *,
+ struct ieee80211_channel *);
+static void rum_enable_tsf_sync(struct rum_softc *);
+static void rum_update_slot(struct ifnet *);
+static void rum_set_bssid(struct rum_softc *, const uint8_t *);
+static void rum_set_macaddr(struct rum_softc *, const uint8_t *);
+static void rum_update_promisc(struct rum_softc *);
+static const char *rum_get_rf(int);
+static void rum_read_eeprom(struct rum_softc *);
+static int rum_bbp_init(struct rum_softc *);
+static void rum_init_locked(struct rum_softc *);
+static void rum_init(void *);
+static void rum_stop(void *);
+static int rum_load_microcode(struct rum_softc *, const u_char *,
+ size_t);
+static int rum_prepare_beacon(struct rum_softc *,
+ struct ieee80211vap *);
+static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rum_newassoc(struct ieee80211_node *, int);
+static void rum_scan_start(struct ieee80211com *);
+static void rum_scan_end(struct ieee80211com *);
+static void rum_set_channel(struct ieee80211com *);
+static int rum_get_rssi(struct rum_softc *, uint8_t);
+static void rum_amrr_start(struct rum_softc *,
+ struct ieee80211_node *);
+static void rum_amrr_timeout(void *);
+static void rum_amrr_update(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+
+static const struct {
+ uint32_t reg;
+ uint32_t val;
+} rum_def_mac[] = {
+ { RT2573_TXRX_CSR0, 0x025fb032 },
+ { RT2573_TXRX_CSR1, 0x9eaa9eaf },
+ { RT2573_TXRX_CSR2, 0x8a8b8c8d },
+ { RT2573_TXRX_CSR3, 0x00858687 },
+ { RT2573_TXRX_CSR7, 0x2e31353b },
+ { RT2573_TXRX_CSR8, 0x2a2a2a2c },
+ { RT2573_TXRX_CSR15, 0x0000000f },
+ { RT2573_MAC_CSR6, 0x00000fff },
+ { RT2573_MAC_CSR8, 0x016c030a },
+ { RT2573_MAC_CSR10, 0x00000718 },
+ { RT2573_MAC_CSR12, 0x00000004 },
+ { RT2573_MAC_CSR13, 0x00007f00 },
+ { RT2573_SEC_CSR0, 0x00000000 },
+ { RT2573_SEC_CSR1, 0x00000000 },
+ { RT2573_SEC_CSR5, 0x00000000 },
+ { RT2573_PHY_CSR1, 0x000023b0 },
+ { RT2573_PHY_CSR5, 0x00040a06 },
+ { RT2573_PHY_CSR6, 0x00080606 },
+ { RT2573_PHY_CSR7, 0x00000408 },
+ { RT2573_AIFSN_CSR, 0x00002273 },
+ { RT2573_CWMIN_CSR, 0x00002344 },
+ { RT2573_CWMAX_CSR, 0x000034aa }
+};
+
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} rum_def_bbp[] = {
+ { 3, 0x80 },
+ { 15, 0x30 },
+ { 17, 0x20 },
+ { 21, 0xc8 },
+ { 22, 0x38 },
+ { 23, 0x06 },
+ { 24, 0xfe },
+ { 25, 0x0a },
+ { 26, 0x0d },
+ { 32, 0x0b },
+ { 34, 0x12 },
+ { 37, 0x07 },
+ { 39, 0xf8 },
+ { 41, 0x60 },
+ { 53, 0x10 },
+ { 54, 0x18 },
+ { 60, 0x10 },
+ { 61, 0x04 },
+ { 62, 0x04 },
+ { 75, 0xfe },
+ { 86, 0xfe },
+ { 88, 0xfe },
+ { 90, 0x0f },
+ { 99, 0x00 },
+ { 102, 0x16 },
+ { 107, 0x04 }
+};
+
+static const struct rfprog {
+ uint8_t chan;
+ uint32_t r1, r2, r3, r4;
+} rum_rf5226[] = {
+ { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 },
+ { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 },
+ { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 },
+ { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 },
+ { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 },
+ { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 },
+ { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 },
+ { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 },
+ { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 },
+ { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 },
+ { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 },
+ { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 },
+ { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 },
+ { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 },
+
+ { 34, 0x00b03, 0x20266, 0x36014, 0x30282 },
+ { 38, 0x00b03, 0x20267, 0x36014, 0x30284 },
+ { 42, 0x00b03, 0x20268, 0x36014, 0x30286 },
+ { 46, 0x00b03, 0x20269, 0x36014, 0x30288 },
+
+ { 36, 0x00b03, 0x00266, 0x26014, 0x30288 },
+ { 40, 0x00b03, 0x00268, 0x26014, 0x30280 },
+ { 44, 0x00b03, 0x00269, 0x26014, 0x30282 },
+ { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 },
+ { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 },
+ { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 },
+ { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 },
+ { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 },
+
+ { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 },
+ { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 },
+ { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 },
+ { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 },
+ { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 },
+ { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 },
+ { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 },
+ { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 },
+ { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 },
+ { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 },
+ { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 },
+
+ { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 },
+ { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 },
+ { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 },
+ { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 },
+ { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 }
+}, rum_rf5225[] = {
+ { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 },
+ { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 },
+ { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 },
+ { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 },
+ { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 },
+ { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 },
+ { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 },
+ { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 },
+ { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 },
+ { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 },
+ { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 },
+ { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 },
+ { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 },
+ { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 },
+
+ { 34, 0x00b33, 0x01266, 0x26014, 0x30282 },
+ { 38, 0x00b33, 0x01267, 0x26014, 0x30284 },
+ { 42, 0x00b33, 0x01268, 0x26014, 0x30286 },
+ { 46, 0x00b33, 0x01269, 0x26014, 0x30288 },
+
+ { 36, 0x00b33, 0x01266, 0x26014, 0x30288 },
+ { 40, 0x00b33, 0x01268, 0x26014, 0x30280 },
+ { 44, 0x00b33, 0x01269, 0x26014, 0x30282 },
+ { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 },
+ { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 },
+ { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 },
+ { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 },
+ { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 },
+
+ { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 },
+ { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 },
+ { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 },
+ { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 },
+ { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 },
+ { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 },
+ { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 },
+ { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 },
+ { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 },
+ { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 },
+ { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 },
+
+ { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 },
+ { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 },
+ { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 },
+ { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 },
+ { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 }
+};
+
+static int
+rum_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return UMATCH_NONE;
+
+ return (usb_lookup(rum_devs, uaa->vendor, uaa->product) != NULL) ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+}
+
+static int
+rum_attach(device_t self)
+{
+ struct rum_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct ieee80211com *ic;
+ struct ifnet *ifp;
+ const uint8_t *ucode = NULL;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status error;
+ int i, ntries, size;
+ uint8_t bands;
+ uint32_t tmp;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+
+ if (usbd_set_config_no(sc->sc_udev, RT2573_CONFIG_NO, 0) != 0) {
+ device_printf(self, "could not set configuration no\n");
+ return ENXIO;
+ }
+
+ /* get the first interface handle */
+ error = usbd_device2interface_handle(sc->sc_udev, RT2573_IFACE_INDEX,
+ &sc->sc_iface);
+ if (error != 0) {
+ device_printf(self, "could not get interface handle\n");
+ return ENXIO;
+ }
+
+ /*
+ * Find endpoints.
+ */
+ id = usbd_get_interface_descriptor(sc->sc_iface);
+
+ sc->sc_rx_no = sc->sc_tx_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(self,
+ "no endpoint descriptor for iface %d\n", i);
+ return ENXIO;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_rx_no = ed->bEndpointAddress;
+ else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_tx_no = ed->bEndpointAddress;
+ }
+ if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
+ device_printf(self, "missing endpoint\n");
+ return ENXIO;
+ }
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(self, "can not if_alloc()\n");
+ return ENXIO;
+ }
+ ic = ifp->if_l2com;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+
+ usb_init_task(&sc->sc_task, rum_task, sc);
+ usb_init_task(&sc->sc_scantask, rum_scantask, sc);
+ callout_init(&sc->watchdog_ch, 0);
+
+ /* retrieve RT2573 rev. no */
+ for (ntries = 0; ntries < 1000; ntries++) {
+ if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0)
+ break;
+ DELAY(1000);
+ }
+ if (ntries == 1000) {
+ device_printf(self, "timeout waiting for chip to settle\n");
+ goto bad;
+ }
+
+ /* retrieve MAC address and various other things from EEPROM */
+ rum_read_eeprom(sc);
+
+ device_printf(self, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
+ tmp, rum_get_rf(sc->rf_rev));
+
+ ucode = rt2573_ucode;
+ size = sizeof rt2573_ucode;
+ error = rum_load_microcode(sc, ucode, size);
+ if (error != 0) {
+ device_printf(self, "could not load 8051 microcode\n");
+ goto bad;
+ }
+
+ ifp->if_softc = sc;
+ if_initname(ifp, "rum", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT; /* USB stack is still under Giant lock */
+ ifp->if_init = rum_init;
+ ifp->if_ioctl = rum_ioctl;
+ ifp->if_start = rum_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode supported */
+ | IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226)
+ setbit(&bands, IEEE80211_MODE_11A);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_newassoc = rum_newassoc;
+ ic->ic_raw_xmit = rum_raw_xmit;
+ ic->ic_node_alloc = rum_node_alloc;
+ ic->ic_scan_start = rum_scan_start;
+ ic->ic_scan_end = rum_scan_end;
+ ic->ic_set_channel = rum_set_channel;
+
+ ic->ic_vap_create = rum_vap_create;
+ ic->ic_vap_delete = rum_vap_delete;
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
+
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT);
+
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return 0;
+bad:
+ mtx_destroy(&sc->sc_mtx);
+ if_free(ifp);
+ return ENXIO;
+}
+
+static int
+rum_detach(device_t self)
+{
+ struct rum_softc *sc = device_get_softc(self);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ rum_stop(sc);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+ callout_stop(&sc->watchdog_ch);
+
+ if (sc->amrr_xfer != NULL) {
+ usbd_free_xfer(sc->amrr_xfer);
+ sc->amrr_xfer = NULL;
+ }
+
+ if (sc->sc_rx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_rx_pipeh);
+ usbd_close_pipe(sc->sc_rx_pipeh);
+ }
+ if (sc->sc_tx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ }
+
+ rum_free_rx_list(sc);
+ rum_free_tx_list(sc);
+
+ if_free(ifp);
+ mtx_destroy(&sc->sc_mtx);
+
+ return 0;
+}
+
+static struct ieee80211vap *
+rum_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct rum_vap *rvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ rvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = rum_newstate;
+
+ callout_init(&rvp->amrr_ch, 0);
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rum_vap_delete(struct ieee80211vap *vap)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ callout_stop(&rvp->amrr_ch);
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
+static int
+rum_alloc_tx_list(struct rum_softc *sc)
+{
+ struct rum_tx_data *data;
+ int i, error;
+
+ sc->tx_queued = sc->tx_cur = 0;
+
+ for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate tx xfer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ data->buf = usbd_alloc_buffer(data->xfer,
+ RT2573_TX_DESC_SIZE + MCLBYTES);
+ if (data->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate tx buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ /* clean Tx descriptor */
+ bzero(data->buf, RT2573_TX_DESC_SIZE);
+ }
+
+ return 0;
+
+fail: rum_free_tx_list(sc);
+ return error;
+}
+
+static void
+rum_free_tx_list(struct rum_softc *sc)
+{
+ struct rum_tx_data *data;
+ int i;
+
+ for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->xfer != NULL) {
+ usbd_free_xfer(data->xfer);
+ data->xfer = NULL;
+ }
+
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static int
+rum_alloc_rx_list(struct rum_softc *sc)
+{
+ struct rum_rx_data *data;
+ int i, error;
+
+ for (i = 0; i < RUM_RX_LIST_COUNT; i++) {
+ data = &sc->rx_data[i];
+
+ data->sc = sc;
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx xfer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (data->m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ data->buf = mtod(data->m, uint8_t *);
+ }
+
+ return 0;
+
+fail: rum_free_rx_list(sc);
+ return error;
+}
+
+static void
+rum_free_rx_list(struct rum_softc *sc)
+{
+ struct rum_rx_data *data;
+ int i;
+
+ for (i = 0; i < RUM_RX_LIST_COUNT; i++) {
+ data = &sc->rx_data[i];
+
+ if (data->xfer != NULL) {
+ usbd_free_xfer(data->xfer);
+ data->xfer = NULL;
+ }
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ }
+}
+
+static void
+rum_task(void *arg)
+{
+ struct rum_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct rum_vap *rvp = RUM_VAP(vap);
+ const struct ieee80211_txparam *tp;
+ enum ieee80211_state ostate;
+ struct ieee80211_node *ni;
+ uint32_t tmp;
+
+ ostate = vap->iv_state;
+
+ RUM_LOCK(sc);
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_RUN) {
+ /* abort TSF synchronization */
+ tmp = rum_read(sc, RT2573_TXRX_CSR9);
+ rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
+ }
+ break;
+
+ case IEEE80211_S_RUN:
+ ni = vap->iv_bss;
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ rum_update_slot(ic->ic_ifp);
+ rum_enable_mrr(sc);
+ rum_set_txpreamble(sc);
+ rum_set_basicrates(sc);
+ rum_set_bssid(sc, ni->ni_bssid);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)
+ rum_prepare_beacon(sc, vap);
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
+ rum_enable_tsf_sync(sc);
+
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ rum_amrr_start(sc, ni);
+ break;
+ default:
+ break;
+ }
+
+ RUM_UNLOCK(sc);
+
+ IEEE80211_LOCK(ic);
+ rvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+}
+
+static int
+rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+ callout_stop(&rvp->amrr_ch);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+
+ if (nstate == IEEE80211_S_INIT) {
+ rvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
+ usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
+ return EINPROGRESS;
+ }
+}
+
+static void
+rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rum_tx_data *data = priv;
+ struct rum_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if (data->m != NULL && data->m->m_flags & M_TXCB)
+ ieee80211_process_callback(data->ni, data->m, 0/*XXX*/);
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
+
+ ifp->if_oerrors++;
+ return;
+ }
+
+ m_freem(data->m);
+ data->m = NULL;
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+
+ sc->tx_queued--;
+ ifp->if_opackets++;
+
+ DPRINTFN(10, ("tx done\n"));
+
+ sc->sc_tx_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ rum_start(ifp);
+}
+
+static void
+rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct rum_rx_data *data = priv;
+ struct rum_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct rum_rx_desc *desc;
+ struct ieee80211_node *ni;
+ struct mbuf *mnew, *m;
+ int len, rssi;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
+ goto skip;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ if (len < RT2573_RX_DESC_SIZE + sizeof (struct ieee80211_frame_min)) {
+ DPRINTF(("%s: xfer too short %d\n",
+ device_get_nameunit(sc->sc_dev), len));
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ desc = (struct rum_rx_desc *)data->buf;
+
+ if (le32toh(desc->flags) & RT2573_RX_CRC_ERROR) {
+ /*
+ * This should not happen since we did not request to receive
+ * those frames when we filled RT2573_TXRX_CSR0.
+ */
+ DPRINTFN(5, ("CRC error\n"));
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (mnew == NULL) {
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ m = data->m;
+ data->m = mnew;
+ data->buf = mtod(data->m, uint8_t *);
+
+ /* finalize mbuf */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_data = (caddr_t)(desc + 1);
+ m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff;
+
+ rssi = rum_get_rssi(sc, desc->rssi);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct rum_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ (desc->flags & htole32(RT2573_RX_OFDM)) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_antenna = sc->rx_ant;
+ tap->wr_antsignal = rssi;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ /* Error happened during RSSI conversion. */
+ if (rssi < 0)
+ rssi = -30; /* XXX ignored by net80211 */
+ (void) ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0);
+
+ DPRINTFN(15, ("rx done\n"));
+
+skip: /* setup a new transfer */
+ usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data, data->buf, MCLBYTES,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rum_rxeof);
+ usbd_transfer(xfer);
+}
+
+static uint8_t
+rum_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+
+static void
+rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
+ uint32_t flags, uint16_t xflags, int len, int rate)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t plcp_length;
+ int remainder;
+
+ desc->flags = htole32(flags);
+ desc->flags |= htole32(RT2573_TX_VALID);
+ desc->flags |= htole32(len << 16);
+
+ desc->xflags = htole16(xflags);
+
+ desc->wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) |
+ RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10));
+
+ /* setup PLCP fields */
+ desc->plcp_signal = rum_plcp_signal(rate);
+ desc->plcp_service = 4;
+
+ len += IEEE80211_CRC_LEN;
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
+ desc->flags |= htole32(RT2573_TX_OFDM);
+
+ plcp_length = len & 0xfff;
+ desc->plcp_length_hi = plcp_length >> 6;
+ desc->plcp_length_lo = plcp_length & 0x3f;
+ } else {
+ plcp_length = (16 * len + rate - 1) / rate;
+ if (rate == 22) {
+ remainder = (16 * len) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= RT2573_PLCP_LENGEXT;
+ }
+ desc->plcp_length_hi = plcp_length >> 8;
+ desc->plcp_length_lo = plcp_length & 0xff;
+
+ if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->plcp_signal |= 0x08;
+ }
+}
+
+#define RUM_TX_TIMEOUT 5000
+
+static int
+rum_sendprot(struct rum_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct rum_tx_desc *desc;
+ struct rum_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+ usbd_status error;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2573_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2573_TX_NEED_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct rum_tx_desc *)data->buf;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ m_copydata(mprot, 0, mprot->m_pkthdr.len,
+ data->buf + RT2573_TX_DESC_SIZE);
+ rum_setup_tx_desc(sc, desc, flags, 0, mprot->m_pkthdr.len, protrate);
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ /* NB: no roundup necessary */
+ RT2573_TX_DESC_SIZE + mprot->m_pkthdr.len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct rum_tx_desc *desc;
+ struct rum_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ uint32_t flags = 0;
+ uint16_t dur;
+ usbd_status error;
+ int xferlen;
+
+ data = &sc->tx_data[sc->tx_cur];
+ data->m = m0;
+ data->ni = ni;
+ desc = (struct rum_tx_desc *)data->buf;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RT2573_TX_NEED_ACK;
+
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
+ flags |= RT2573_TX_TIMESTAMP;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = tp->mgmtrate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
+ rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate);
+
+ /* align end on a 4-bytes boundary */
+ xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3;
+
+ /*
+ * No space left in the last URB to store the extra 4 bytes, force
+ * sending of another URB.
+ */
+ if ((xferlen % 64) == 0)
+ xferlen += 4;
+
+ DPRINTFN(10, ("sending mgt frame len=%d rate=%d xfer len=%d\n",
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate, xferlen));
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ m_freem(m0);
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct rum_tx_desc *desc;
+ struct rum_tx_data *data;
+ uint32_t flags;
+ usbd_status error;
+ int xferlen, rate;
+
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct rum_tx_desc *)data->buf;
+
+ rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
+ /* XXX validate */
+ if (rate == 0) {
+ m_freem(m0);
+ return EINVAL;
+ }
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RT2573_TX_NEED_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = rum_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ data->m = m0;
+ data->ni = ni;
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
+ /* XXX need to setup descriptor ourself */
+ rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate);
+
+ /* align end on a 4-bytes boundary */
+ xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3;
+
+ /*
+ * No space left in the last URB to store the extra 4 bytes, force
+ * sending of another URB.
+ */
+ if ((xferlen % 64) == 0)
+ xferlen += 4;
+
+ DPRINTFN(10, ("sending raw frame len=%u rate=%u xfer len=%u\n",
+ m0->m_pkthdr.len, rate, xferlen));
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT,
+ rum_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS)
+ return error;
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct rum_tx_desc *desc;
+ struct rum_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ uint32_t flags = 0;
+ uint16_t dur;
+ usbd_status error;
+ int rate, xferlen;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else {
+ (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn);
+ rate = ni->ni_txrate;
+ }
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rum_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+ }
+
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct rum_tx_desc *)data->buf;
+
+ data->m = m0;
+ data->ni = ni;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RT2573_TX_NEED_ACK;
+ flags |= RT2573_TX_MORE_FRAG;
+
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
+ rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate);
+
+ /* align end on a 4-bytes boundary */
+ xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3;
+
+ /*
+ * No space left in the last URB to store the extra 4 bytes, force
+ * sending of another URB.
+ */
+ if ((xferlen % 64) == 0)
+ xferlen += 4;
+
+ DPRINTFN(10, ("sending frame len=%d rate=%d xfer len=%d\n",
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate, xferlen));
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ m_freem(m0);
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static void
+rum_start(struct ifnet *ifp)
+{
+ struct rum_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (rum_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc);
+ }
+}
+
+static void
+rum_watchdog(void *arg)
+{
+ struct rum_softc *sc = arg;
+
+ RUM_LOCK(sc);
+
+ if (sc->sc_tx_timer > 0) {
+ if (--sc->sc_tx_timer == 0) {
+ device_printf(sc->sc_dev, "device timeout\n");
+ /*rum_init(ifp); XXX needs a process context! */
+ sc->sc_ifp->if_oerrors++;
+ RUM_UNLOCK(sc);
+ return;
+ }
+ callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc);
+ }
+
+ RUM_UNLOCK(sc);
+}
+
+static int
+rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct rum_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ RUM_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ rum_init(sc);
+ startall = 1;
+ } else
+ rum_update_promisc(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ rum_stop(sc);
+ }
+ RUM_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCGIFADDR:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static void
+rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2573_READ_EEPROM;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static uint32_t
+rum_read(struct rum_softc *sc, uint16_t reg)
+{
+ uint32_t val;
+
+ rum_read_multi(sc, reg, &val, sizeof val);
+
+ return le32toh(val);
+}
+
+static void
+rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2573_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not multi read MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val)
+{
+ uint32_t tmp = htole32(val);
+
+ rum_write_multi(sc, reg, &tmp, sizeof tmp);
+}
+
+static void
+rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2573_WRITE_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not multi write MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 5; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY))
+ break;
+ }
+ if (ntries == 5) {
+ device_printf(sc->sc_dev, "could not write to BBP\n");
+ return;
+ }
+
+ tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val;
+ rum_write(sc, RT2573_PHY_CSR3, tmp);
+}
+
+static uint8_t
+rum_bbp_read(struct rum_softc *sc, uint8_t reg)
+{
+ uint32_t val;
+ int ntries;
+
+ for (ntries = 0; ntries < 5; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY))
+ break;
+ }
+ if (ntries == 5) {
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+ }
+
+ val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8;
+ rum_write(sc, RT2573_PHY_CSR3, val);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ val = rum_read(sc, RT2573_PHY_CSR3);
+ if (!(val & RT2573_BBP_BUSY))
+ return val & 0xff;
+ DELAY(1);
+ }
+
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+}
+
+static void
+rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 5; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY))
+ break;
+ }
+ if (ntries == 5) {
+ device_printf(sc->sc_dev, "could not write to RF\n");
+ return;
+ }
+
+ tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 |
+ (reg & 3);
+ rum_write(sc, RT2573_PHY_CSR4, tmp);
+
+ /* remember last written value in sc */
+ sc->rf_regs[reg] = val;
+
+ DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff));
+}
+
+static void
+rum_select_antenna(struct rum_softc *sc)
+{
+ uint8_t bbp4, bbp77;
+ uint32_t tmp;
+
+ bbp4 = rum_bbp_read(sc, 4);
+ bbp77 = rum_bbp_read(sc, 77);
+
+ /* TBD */
+
+ /* make sure Rx is disabled before switching antenna */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0);
+ rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
+
+ rum_bbp_write(sc, 4, bbp4);
+ rum_bbp_write(sc, 77, bbp77);
+
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+}
+
+/*
+ * Enable multi-rate retries for frames sent at OFDM rates.
+ * In 802.11b/g mode, allow fallback to CCK rates.
+ */
+static void
+rum_enable_mrr(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t tmp;
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR4);
+
+ tmp &= ~RT2573_MRR_CCK_FALLBACK;
+ if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
+ tmp |= RT2573_MRR_CCK_FALLBACK;
+ tmp |= RT2573_MRR_ENABLED;
+
+ rum_write(sc, RT2573_TXRX_CSR4, tmp);
+}
+
+static void
+rum_set_txpreamble(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t tmp;
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR4);
+
+ tmp &= ~RT2573_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ tmp |= RT2573_SHORT_PREAMBLE;
+
+ rum_write(sc, RT2573_TXRX_CSR4, tmp);
+}
+
+static void
+rum_set_basicrates(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ /* update basic rate set */
+ if (ic->ic_curmode == IEEE80211_MODE_11B) {
+ /* 11b basic rates: 1, 2Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0x3);
+ } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) {
+ /* 11a basic rates: 6, 12, 24Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0x150);
+ } else {
+ /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0xf);
+ }
+}
+
+/*
+ * Reprogram MAC/BBP to switch to a new band. Values taken from the reference
+ * driver.
+ */
+static void
+rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c)
+{
+ uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104;
+ uint32_t tmp;
+
+ /* update all BBP registers that depend on the band */
+ bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c;
+ bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48;
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c;
+ bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10;
+ }
+ if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+ (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+ bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10;
+ }
+
+ sc->bbp17 = bbp17;
+ rum_bbp_write(sc, 17, bbp17);
+ rum_bbp_write(sc, 96, bbp96);
+ rum_bbp_write(sc, 104, bbp104);
+
+ if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+ (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+ rum_bbp_write(sc, 75, 0x80);
+ rum_bbp_write(sc, 86, 0x80);
+ rum_bbp_write(sc, 88, 0x80);
+ }
+
+ rum_bbp_write(sc, 35, bbp35);
+ rum_bbp_write(sc, 97, bbp97);
+ rum_bbp_write(sc, 98, bbp98);
+
+ tmp = rum_read(sc, RT2573_PHY_CSR0);
+ tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ);
+ if (IEEE80211_IS_CHAN_2GHZ(c))
+ tmp |= RT2573_PA_PE_2GHZ;
+ else
+ tmp |= RT2573_PA_PE_5GHZ;
+ rum_write(sc, RT2573_PHY_CSR0, tmp);
+}
+
+static void
+rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ const struct rfprog *rfprog;
+ uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT;
+ int8_t power;
+ u_int i, chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return;
+
+ /* select the appropriate RF settings based on what EEPROM says */
+ rfprog = (sc->rf_rev == RT2573_RF_5225 ||
+ sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rfprog[i].chan != chan; i++);
+
+ power = sc->txpow[i];
+ if (power < 0) {
+ bbp94 += power;
+ power = 0;
+ } else if (power > 31) {
+ bbp94 += power - 31;
+ power = 31;
+ }
+
+ /*
+ * If we are switching from the 2GHz band to the 5GHz band or
+ * vice-versa, BBP registers need to be reprogrammed.
+ */
+ if (c->ic_flags != ic->ic_curchan->ic_flags) {
+ rum_select_band(sc, c);
+ rum_select_antenna(sc);
+ }
+ ic->ic_curchan = c;
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ DELAY(10);
+
+ /* enable smart mode for MIMO-capable RFs */
+ bbp3 = rum_bbp_read(sc, 3);
+
+ bbp3 &= ~RT2573_SMART_MODE;
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527)
+ bbp3 |= RT2573_SMART_MODE;
+
+ rum_bbp_write(sc, 3, bbp3);
+
+ if (bbp94 != RT2573_BBPR94_DEFAULT)
+ rum_bbp_write(sc, 94, bbp94);
+}
+
+/*
+ * Enable TSF synchronization and tell h/w to start sending beacons for IBSS
+ * and HostAP operating modes.
+ */
+static void
+rum_enable_tsf_sync(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+
+ if (vap->iv_opmode != IEEE80211_M_STA) {
+ /*
+ * Change default 16ms TBTT adjustment to 8ms.
+ * Must be done before enabling beacon generation.
+ */
+ rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8);
+ }
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000;
+
+ /* set beacon interval (in 1/16ms unit) */
+ tmp |= vap->iv_bss->ni_intval * 16;
+
+ tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ tmp |= RT2573_TSF_MODE(1);
+ else
+ tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON;
+
+ rum_write(sc, RT2573_TXRX_CSR9, tmp);
+}
+
+static void
+rum_update_slot(struct ifnet *ifp)
+{
+ struct rum_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint8_t slottime;
+ uint32_t tmp;
+
+ slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
+
+ tmp = rum_read(sc, RT2573_MAC_CSR9);
+ tmp = (tmp & ~0xff) | slottime;
+ rum_write(sc, RT2573_MAC_CSR9, tmp);
+
+ DPRINTF(("setting slot time to %uus\n", slottime));
+}
+
+static void
+rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid)
+{
+ uint32_t tmp;
+
+ tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24;
+ rum_write(sc, RT2573_MAC_CSR4, tmp);
+
+ tmp = bssid[4] | bssid[5] << 8 | RT2573_ONE_BSSID << 16;
+ rum_write(sc, RT2573_MAC_CSR5, tmp);
+}
+
+static void
+rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr)
+{
+ uint32_t tmp;
+
+ tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24;
+ rum_write(sc, RT2573_MAC_CSR2, tmp);
+
+ tmp = addr[4] | addr[5] << 8 | 0xff << 16;
+ rum_write(sc, RT2573_MAC_CSR3, tmp);
+}
+
+static void
+rum_update_promisc(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ uint32_t tmp;
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR0);
+
+ tmp &= ~RT2573_DROP_NOT_TO_ME;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RT2573_DROP_NOT_TO_ME;
+
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+
+ DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving"));
+}
+
+static const char *
+rum_get_rf(int rev)
+{
+ switch (rev) {
+ case RT2573_RF_2527: return "RT2527 (MIMO XR)";
+ case RT2573_RF_2528: return "RT2528";
+ case RT2573_RF_5225: return "RT5225 (MIMO XR)";
+ case RT2573_RF_5226: return "RT5226";
+ default: return "unknown";
+ }
+}
+
+static void
+rum_read_eeprom(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t val;
+#ifdef RUM_DEBUG
+ int i;
+#endif
+
+ /* read MAC address */
+ rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, ic->ic_myaddr, 6);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2);
+ val = le16toh(val);
+ sc->rf_rev = (val >> 11) & 0x1f;
+ sc->hw_radio = (val >> 10) & 0x1;
+ sc->rx_ant = (val >> 4) & 0x3;
+ sc->tx_ant = (val >> 2) & 0x3;
+ sc->nb_ant = val & 0x3;
+
+ DPRINTF(("RF revision=%d\n", sc->rf_rev));
+
+ rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2);
+ val = le16toh(val);
+ sc->ext_5ghz_lna = (val >> 6) & 0x1;
+ sc->ext_2ghz_lna = (val >> 4) & 0x1;
+
+ DPRINTF(("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
+ sc->ext_2ghz_lna, sc->ext_5ghz_lna));
+
+ rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */
+
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10)
+ sc->rssi_2ghz_corr = 0;
+
+ rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */
+
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10)
+ sc->rssi_5ghz_corr = 0;
+
+ if (sc->ext_2ghz_lna)
+ sc->rssi_2ghz_corr -= 14;
+ if (sc->ext_5ghz_lna)
+ sc->rssi_5ghz_corr -= 14;
+
+ DPRINTF(("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
+ sc->rssi_2ghz_corr, sc->rssi_5ghz_corr));
+
+ rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rffreq = val & 0xff;
+
+ DPRINTF(("RF freq=%d\n", sc->rffreq));
+
+ /* read Tx power for all a/b/g channels */
+ rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14);
+ /* XXX default Tx power for 802.11a channels */
+ memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14);
+#ifdef RUM_DEBUG
+ for (i = 0; i < 14; i++)
+ DPRINTF(("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]));
+#endif
+
+ /* read default values for BBP registers */
+ rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16);
+#ifdef RUM_DEBUG
+ for (i = 0; i < 14; i++) {
+ if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff)
+ continue;
+ DPRINTF(("BBP R%d=%02x\n", sc->bbp_prom[i].reg,
+ sc->bbp_prom[i].val));
+ }
+#endif
+}
+
+static int
+rum_bbp_init(struct rum_softc *sc)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ int i, ntries;
+
+ /* wait for BBP to be ready */
+ for (ntries = 0; ntries < 100; ntries++) {
+ const uint8_t val = rum_bbp_read(sc, 0);
+ if (val != 0 && val != 0xff)
+ break;
+ DELAY(1000);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+ return EIO;
+ }
+
+ /* initialize BBP registers to default values */
+ for (i = 0; i < N(rum_def_bbp); i++)
+ rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val);
+
+ /* write vendor-specific BBP values (from EEPROM) */
+ for (i = 0; i < 16; i++) {
+ if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff)
+ continue;
+ rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+ }
+
+ return 0;
+#undef N
+}
+
+static void
+rum_init_locked(struct rum_softc *sc)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct rum_rx_data *data;
+ uint32_t tmp;
+ usbd_status error;
+ int i, ntries;
+
+ rum_stop(sc);
+
+ /* initialize MAC registers to default values */
+ for (i = 0; i < N(rum_def_mac); i++)
+ rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val);
+
+ /* set host ready */
+ rum_write(sc, RT2573_MAC_CSR1, 3);
+ rum_write(sc, RT2573_MAC_CSR1, 0);
+
+ /* wait for BBP/RF to wakeup */
+ for (ntries = 0; ntries < 1000; ntries++) {
+ if (rum_read(sc, RT2573_MAC_CSR12) & 8)
+ break;
+ rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */
+ DELAY(1000);
+ }
+ if (ntries == 1000) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
+ goto fail;
+ }
+
+ if ((error = rum_bbp_init(sc)) != 0)
+ goto fail;
+
+ /* select default channel */
+ rum_select_band(sc, ic->ic_curchan);
+ rum_select_antenna(sc);
+ rum_set_chan(sc, ic->ic_curchan);
+
+ /* clear STA registers */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ rum_set_macaddr(sc, ic->ic_myaddr);
+
+ /* initialize ASIC */
+ rum_write(sc, RT2573_MAC_CSR1, 4);
+
+ /*
+ * Allocate xfer for AMRR statistics requests.
+ */
+ sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->amrr_xfer == NULL) {
+ device_printf(sc->sc_dev, "could not allocate AMRR xfer\n");
+ goto fail;
+ }
+
+ /*
+ * Open Tx and Rx USB bulk pipes.
+ */
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_tx_pipeh);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not open Tx pipe: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_rx_pipeh);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not open Rx pipe: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ error = rum_alloc_tx_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Tx list\n");
+ goto fail;
+ }
+ error = rum_alloc_rx_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Rx list\n");
+ goto fail;
+ }
+
+ /*
+ * Start up the receive pipe.
+ */
+ for (i = 0; i < RUM_RX_LIST_COUNT; i++) {
+ data = &sc->rx_data[i];
+
+ usbd_setup_xfer(data->xfer, sc->sc_rx_pipeh, data, data->buf,
+ MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, rum_rxeof);
+ usbd_transfer(data->xfer);
+ }
+
+ /* update Rx filter */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff;
+
+ tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR |
+ RT2573_DROP_ACKCTS;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ tmp |= RT2573_DROP_TODS;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RT2573_DROP_NOT_TO_ME;
+ }
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ return;
+
+fail: rum_stop(sc);
+#undef N
+}
+
+static void
+rum_init(void *priv)
+{
+ struct rum_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RUM_LOCK(sc);
+ rum_init_locked(sc);
+ RUM_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+rum_stop(void *priv)
+{
+ struct rum_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ uint32_t tmp;
+
+ sc->sc_tx_timer = 0;
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ /* disable Rx */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0);
+ rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
+
+ /* reset ASIC */
+ rum_write(sc, RT2573_MAC_CSR1, 3);
+ rum_write(sc, RT2573_MAC_CSR1, 0);
+
+ if (sc->amrr_xfer != NULL) {
+ usbd_free_xfer(sc->amrr_xfer);
+ sc->amrr_xfer = NULL;
+ }
+
+ if (sc->sc_rx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_rx_pipeh);
+ usbd_close_pipe(sc->sc_rx_pipeh);
+ sc->sc_rx_pipeh = NULL;
+ }
+ if (sc->sc_tx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ sc->sc_tx_pipeh = NULL;
+ }
+
+ rum_free_rx_list(sc);
+ rum_free_tx_list(sc);
+}
+
+static int
+rum_load_microcode(struct rum_softc *sc, const u_char *ucode, size_t size)
+{
+ usb_device_request_t req;
+ uint16_t reg = RT2573_MCU_CODE_BASE;
+ usbd_status error;
+
+ /* copy firmware image into NIC */
+ for (; size >= 4; reg += 4, ucode += 4, size -= 4)
+ rum_write(sc, reg, UGETDW(ucode));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2573_MCU_CNTL;
+ USETW(req.wValue, RT2573_MCU_RUN);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ error = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not run firmware: %s\n",
+ usbd_errstr(error));
+ }
+ return error;
+}
+
+static int
+rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
+ struct rum_tx_desc desc;
+ struct mbuf *m0;
+
+ m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo);
+ if (m0 == NULL) {
+ return ENOBUFS;
+ }
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ,
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ /* copy the first 24 bytes of Tx descriptor into NIC memory */
+ rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24);
+
+ /* copy beacon header and payload into NIC memory */
+ rum_write_multi(sc, RT2573_HW_BEACON_BASE0 + 24, mtod(m0, uint8_t *),
+ m0->m_pkthdr.len);
+
+ m_freem(m0);
+
+ return 0;
+}
+
+static int
+rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ifnet *ifp = ni->ni_ic->ic_ifp;
+ struct rum_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return EIO;
+ }
+
+ ifp->if_opackets++;
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ if (rum_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ if (rum_tx_raw(sc, m, ni, params) != 0)
+ goto bad;
+ }
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc);
+
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ return EIO;
+}
+
+static void
+rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR5) */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni);
+
+ callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap);
+}
+
+static void
+rum_amrr_timeout(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ usb_device_request_t req;
+
+ /*
+ * Asynchronously read statistic registers (cleared by read).
+ */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2573_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, RT2573_STA_CSR0);
+ USETW(req.wLength, sizeof sc->sta);
+
+ usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap,
+ USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0,
+ rum_amrr_update);
+ (void)usbd_transfer(sc->amrr_xfer);
+}
+
+static void
+rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct ieee80211vap *vap = priv;
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct rum_softc *sc = ifp->if_softc;
+ int ok, fail;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ device_printf(sc->sc_dev, "could not retrieve Tx statistics - "
+ "cancelling automatic rate control\n");
+ return;
+ }
+
+ ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */
+ (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */
+ fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */
+
+ ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn,
+ ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail);
+
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
+
+ callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap);
+}
+
+/* ARGUSED */
+static struct ieee80211_node *
+rum_node_alloc(struct ieee80211vap *vap __unused,
+ const uint8_t mac[IEEE80211_ADDR_LEN] __unused)
+{
+ struct rum_node *rn;
+
+ rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return rn != NULL ? &rn->ni : NULL;
+}
+
+static void
+rum_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni);
+}
+
+static void
+rum_scan_start(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SCAN_START;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+rum_scan_end(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SCAN_END;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+rum_set_channel(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SET_CHANNEL;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+}
+
+static void
+rum_scantask(void *arg)
+{
+ struct rum_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+
+ RUM_LOCK(sc);
+
+ switch (sc->sc_scan_action) {
+ case RUM_SCAN_START:
+ /* abort TSF synchronization */
+ tmp = rum_read(sc, RT2573_TXRX_CSR9);
+ rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
+ rum_set_bssid(sc, ifp->if_broadcastaddr);
+ break;
+
+ case RUM_SCAN_END:
+ rum_enable_tsf_sync(sc);
+ /* XXX keep local copy */
+ rum_set_bssid(sc, vap->iv_bss->ni_bssid);
+ break;
+
+ case RUM_SET_CHANNEL:
+ mtx_lock(&Giant);
+ rum_set_chan(sc, ic->ic_curchan);
+ mtx_unlock(&Giant);
+ break;
+
+ default:
+ panic("unknown scan action %d\n", sc->sc_scan_action);
+ /* NEVER REACHED */
+ break;
+ }
+
+ RUM_UNLOCK(sc);
+}
+
+static int
+rum_get_rssi(struct rum_softc *sc, uint8_t raw)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ int lna, agc, rssi;
+
+ lna = (raw >> 5) & 0x3;
+ agc = raw & 0x1f;
+
+ if (lna == 0) {
+ /*
+ * No RSSI mapping
+ *
+ * NB: Since RSSI is relative to noise floor, -1 is
+ * adequate for caller to know error happened.
+ */
+ return -1;
+ }
+
+ rssi = (2 * agc) - RT2573_NOISE_FLOOR;
+
+ if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
+ rssi += sc->rssi_2ghz_corr;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 74;
+ else if (lna == 3)
+ rssi -= 90;
+ } else {
+ rssi += sc->rssi_5ghz_corr;
+
+ if (!sc->ext_5ghz_lna && lna != 1)
+ rssi += 4;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 86;
+ else if (lna == 3)
+ rssi -= 100;
+ }
+ return rssi;
+}
+
+static device_method_t rum_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rum_match),
+ DEVMETHOD(device_attach, rum_attach),
+ DEVMETHOD(device_detach, rum_detach),
+
+ { 0, 0 }
+};
+
+static driver_t rum_driver = {
+ "rum",
+ rum_methods,
+ sizeof(struct rum_softc)
+};
+
+static devclass_t rum_devclass;
+
+DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/if_rumreg.h b/sys/legacy/dev/usb/if_rumreg.h
new file mode 100644
index 0000000..75a51bc
--- /dev/null
+++ b/sys/legacy/dev/usb/if_rumreg.h
@@ -0,0 +1,235 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RT2573_NOISE_FLOOR -95
+
+#define RT2573_TX_DESC_SIZE (sizeof (struct rum_tx_desc))
+#define RT2573_RX_DESC_SIZE (sizeof (struct rum_rx_desc))
+
+#define RT2573_CONFIG_NO 1
+#define RT2573_IFACE_INDEX 0
+
+#define RT2573_MCU_CNTL 0x01
+#define RT2573_WRITE_MAC 0x02
+#define RT2573_READ_MAC 0x03
+#define RT2573_WRITE_MULTI_MAC 0x06
+#define RT2573_READ_MULTI_MAC 0x07
+#define RT2573_READ_EEPROM 0x09
+#define RT2573_WRITE_LED 0x0a
+
+/*
+ * Control and status registers.
+ */
+#define RT2573_AIFSN_CSR 0x0400
+#define RT2573_CWMIN_CSR 0x0404
+#define RT2573_CWMAX_CSR 0x0408
+#define RT2573_MCU_CODE_BASE 0x0800
+#define RT2573_HW_BEACON_BASE0 0x2400
+#define RT2573_MAC_CSR0 0x3000
+#define RT2573_MAC_CSR1 0x3004
+#define RT2573_MAC_CSR2 0x3008
+#define RT2573_MAC_CSR3 0x300c
+#define RT2573_MAC_CSR4 0x3010
+#define RT2573_MAC_CSR5 0x3014
+#define RT2573_MAC_CSR6 0x3018
+#define RT2573_MAC_CSR7 0x301c
+#define RT2573_MAC_CSR8 0x3020
+#define RT2573_MAC_CSR9 0x3024
+#define RT2573_MAC_CSR10 0x3028
+#define RT2573_MAC_CSR11 0x302c
+#define RT2573_MAC_CSR12 0x3030
+#define RT2573_MAC_CSR13 0x3034
+#define RT2573_MAC_CSR14 0x3038
+#define RT2573_MAC_CSR15 0x303c
+#define RT2573_TXRX_CSR0 0x3040
+#define RT2573_TXRX_CSR1 0x3044
+#define RT2573_TXRX_CSR2 0x3048
+#define RT2573_TXRX_CSR3 0x304c
+#define RT2573_TXRX_CSR4 0x3050
+#define RT2573_TXRX_CSR5 0x3054
+#define RT2573_TXRX_CSR6 0x3058
+#define RT2573_TXRX_CSR7 0x305c
+#define RT2573_TXRX_CSR8 0x3060
+#define RT2573_TXRX_CSR9 0x3064
+#define RT2573_TXRX_CSR10 0x3068
+#define RT2573_TXRX_CSR11 0x306c
+#define RT2573_TXRX_CSR12 0x3070
+#define RT2573_TXRX_CSR13 0x3074
+#define RT2573_TXRX_CSR14 0x3078
+#define RT2573_TXRX_CSR15 0x307c
+#define RT2573_PHY_CSR0 0x3080
+#define RT2573_PHY_CSR1 0x3084
+#define RT2573_PHY_CSR2 0x3088
+#define RT2573_PHY_CSR3 0x308c
+#define RT2573_PHY_CSR4 0x3090
+#define RT2573_PHY_CSR5 0x3094
+#define RT2573_PHY_CSR6 0x3098
+#define RT2573_PHY_CSR7 0x309c
+#define RT2573_SEC_CSR0 0x30a0
+#define RT2573_SEC_CSR1 0x30a4
+#define RT2573_SEC_CSR2 0x30a8
+#define RT2573_SEC_CSR3 0x30ac
+#define RT2573_SEC_CSR4 0x30b0
+#define RT2573_SEC_CSR5 0x30b4
+#define RT2573_STA_CSR0 0x30c0
+#define RT2573_STA_CSR1 0x30c4
+#define RT2573_STA_CSR2 0x30c8
+#define RT2573_STA_CSR3 0x30cc
+#define RT2573_STA_CSR4 0x30d0
+#define RT2573_STA_CSR5 0x30d4
+
+
+/* possible flags for register RT2573_MAC_CSR1 */
+#define RT2573_RESET_ASIC (1 << 0)
+#define RT2573_RESET_BBP (1 << 1)
+#define RT2573_HOST_READY (1 << 2)
+
+/* possible flags for register MAC_CSR5 */
+#define RT2573_ONE_BSSID 3
+
+/* possible flags for register TXRX_CSR0 */
+/* Tx filter flags are in the low 16 bits */
+#define RT2573_AUTO_TX_SEQ (1 << 15)
+/* Rx filter flags are in the high 16 bits */
+#define RT2573_DISABLE_RX (1 << 16)
+#define RT2573_DROP_CRC_ERROR (1 << 17)
+#define RT2573_DROP_PHY_ERROR (1 << 18)
+#define RT2573_DROP_CTL (1 << 19)
+#define RT2573_DROP_NOT_TO_ME (1 << 20)
+#define RT2573_DROP_TODS (1 << 21)
+#define RT2573_DROP_VER_ERROR (1 << 22)
+#define RT2573_DROP_MULTICAST (1 << 23)
+#define RT2573_DROP_BROADCAST (1 << 24)
+#define RT2573_DROP_ACKCTS (1 << 25)
+
+/* possible flags for register TXRX_CSR4 */
+#define RT2573_SHORT_PREAMBLE (1 << 18)
+#define RT2573_MRR_ENABLED (1 << 19)
+#define RT2573_MRR_CCK_FALLBACK (1 << 22)
+
+/* possible flags for register TXRX_CSR9 */
+#define RT2573_TSF_TICKING (1 << 16)
+#define RT2573_TSF_MODE(x) (((x) & 0x3) << 17)
+/* TBTT stands for Target Beacon Transmission Time */
+#define RT2573_ENABLE_TBTT (1 << 19)
+#define RT2573_GENERATE_BEACON (1 << 20)
+
+/* possible flags for register PHY_CSR0 */
+#define RT2573_PA_PE_2GHZ (1 << 16)
+#define RT2573_PA_PE_5GHZ (1 << 17)
+
+/* possible flags for register PHY_CSR3 */
+#define RT2573_BBP_READ (1 << 15)
+#define RT2573_BBP_BUSY (1 << 16)
+/* possible flags for register PHY_CSR4 */
+#define RT2573_RF_20BIT (20 << 24)
+#define RT2573_RF_BUSY (1 << 31)
+
+/* LED values */
+#define RT2573_LED_RADIO (1 << 8)
+#define RT2573_LED_G (1 << 9)
+#define RT2573_LED_A (1 << 10)
+#define RT2573_LED_ON 0x1e1e
+#define RT2573_LED_OFF 0x0
+
+#define RT2573_MCU_RUN (1 << 3)
+
+#define RT2573_SMART_MODE (1 << 0)
+
+#define RT2573_BBPR94_DEFAULT 6
+
+#define RT2573_BBP_WRITE (1 << 15)
+
+/* dual-band RF */
+#define RT2573_RF_5226 1
+#define RT2573_RF_5225 3
+/* single-band RF */
+#define RT2573_RF_2528 2
+#define RT2573_RF_2527 4
+
+#define RT2573_BBP_VERSION 0
+
+struct rum_tx_desc {
+ uint32_t flags;
+#define RT2573_TX_BURST (1 << 0)
+#define RT2573_TX_VALID (1 << 1)
+#define RT2573_TX_MORE_FRAG (1 << 2)
+#define RT2573_TX_NEED_ACK (1 << 3)
+#define RT2573_TX_TIMESTAMP (1 << 4)
+#define RT2573_TX_OFDM (1 << 5)
+#define RT2573_TX_IFS_SIFS (1 << 6)
+#define RT2573_TX_LONG_RETRY (1 << 7)
+
+ uint16_t wme;
+#define RT2573_QID(v) (v)
+#define RT2573_AIFSN(v) ((v) << 4)
+#define RT2573_LOGCWMIN(v) ((v) << 8)
+#define RT2573_LOGCWMAX(v) ((v) << 12)
+
+ uint16_t xflags;
+#define RT2573_TX_HWSEQ (1 << 12)
+
+ uint8_t plcp_signal;
+ uint8_t plcp_service;
+#define RT2573_PLCP_LENGEXT 0x80
+
+ uint8_t plcp_length_lo;
+ uint8_t plcp_length_hi;
+
+ uint32_t iv;
+ uint32_t eiv;
+
+ uint8_t offset;
+ uint8_t qid;
+ uint8_t txpower;
+#define RT2573_DEFAULT_TXPOWER 0
+
+ uint8_t reserved;
+} __packed;
+
+struct rum_rx_desc {
+ uint32_t flags;
+#define RT2573_RX_BUSY (1 << 0)
+#define RT2573_RX_DROP (1 << 1)
+#define RT2573_RX_CRC_ERROR (1 << 6)
+#define RT2573_RX_OFDM (1 << 7)
+
+ uint8_t rate;
+ uint8_t rssi;
+ uint8_t reserved1;
+ uint8_t offset;
+ uint32_t iv;
+ uint32_t eiv;
+ uint32_t reserved2[2];
+} __packed;
+
+#define RT2573_RF1 0
+#define RT2573_RF2 2
+#define RT2573_RF3 1
+#define RT2573_RF4 3
+
+#define RT2573_EEPROM_MACBBP 0x0000
+#define RT2573_EEPROM_ADDRESS 0x0004
+#define RT2573_EEPROM_ANTENNA 0x0020
+#define RT2573_EEPROM_CONFIG2 0x0022
+#define RT2573_EEPROM_BBP_BASE 0x0026
+#define RT2573_EEPROM_TXPOWER 0x0046
+#define RT2573_EEPROM_FREQ_OFFSET 0x005e
+#define RT2573_EEPROM_RSSI_2GHZ_OFFSET 0x009a
+#define RT2573_EEPROM_RSSI_5GHZ_OFFSET 0x009c
diff --git a/sys/legacy/dev/usb/if_rumvar.h b/sys/legacy/dev/usb/if_rumvar.h
new file mode 100644
index 0000000..59980c0
--- /dev/null
+++ b/sys/legacy/dev/usb/if_rumvar.h
@@ -0,0 +1,161 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RUM_RX_LIST_COUNT 1
+#define RUM_TX_LIST_COUNT 8
+
+struct rum_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ uint8_t wr_antenna;
+ uint8_t wr_antsignal;
+};
+
+#define RT2573_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct rum_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_antenna;
+};
+
+#define RT2573_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct rum_softc;
+
+struct rum_tx_data {
+ struct rum_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+};
+
+struct rum_rx_data {
+ struct rum_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct mbuf *m;
+};
+
+struct rum_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define RUM_NODE(ni) ((struct rum_node *)(ni))
+
+struct rum_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct callout amrr_ch;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RUM_VAP(vap) ((struct rum_vap *)(vap))
+
+struct rum_softc {
+ struct ifnet *sc_ifp;
+ const struct ieee80211_rate_table *sc_rates;
+
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+
+ int sc_rx_no;
+ int sc_tx_no;
+
+ uint8_t rf_rev;
+ uint8_t rffreq;
+
+ usbd_xfer_handle amrr_xfer;
+
+ usbd_pipe_handle sc_rx_pipeh;
+ usbd_pipe_handle sc_tx_pipeh;
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ struct usb_task sc_task;
+
+ struct usb_task sc_scantask;
+ int sc_scan_action;
+#define RUM_SCAN_START 0
+#define RUM_SCAN_END 1
+#define RUM_SET_CHANNEL 2
+
+ struct rum_rx_data rx_data[RUM_RX_LIST_COUNT];
+ struct rum_tx_data tx_data[RUM_TX_LIST_COUNT];
+ int tx_queued;
+ int tx_cur;
+
+ struct mtx sc_mtx;
+
+ struct callout watchdog_ch;
+
+ int sc_tx_timer;
+
+ uint32_t sta[6];
+ uint32_t rf_regs[4];
+ uint8_t txpow[44];
+
+ struct {
+ uint8_t val;
+ uint8_t reg;
+ } __packed bbp_prom[16];
+
+ int hw_radio;
+ int rx_ant;
+ int tx_ant;
+ int nb_ant;
+ int ext_2ghz_lna;
+ int ext_5ghz_lna;
+ int rssi_2ghz_corr;
+ int rssi_5ghz_corr;
+ uint8_t bbp17;
+
+ struct rum_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+
+ struct rum_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#if 0
+#define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#else
+#define RUM_LOCK(sc) do { ((sc) = (sc)); mtx_lock(&Giant); } while (0)
+#define RUM_UNLOCK(sc) mtx_unlock(&Giant)
+#endif
diff --git a/sys/legacy/dev/usb/if_udav.c b/sys/legacy/dev/usb/if_udav.c
new file mode 100644
index 0000000..e13170e
--- /dev/null
+++ b/sys/legacy/dev/usb/if_udav.c
@@ -0,0 +1,1962 @@
+/* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */
+/* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.org>. All rights reserved.
+ *
+ * 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. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+/*
+ * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY)
+ * The spec can be found at the following url.
+ * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf
+ */
+
+/*
+ * TODO:
+ * Interrupt Endpoint support
+ * External PHYs
+ * powerhook() support?
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+#if defined(__NetBSD__)
+#include "opt_ns.h"
+#endif
+#if defined(__NetBSD__)
+#include "bpfilter.h"
+#endif
+#if defined(__FreeBSD__)
+#define NBPFILTER 1
+#endif
+#if defined(__NetBSD__)
+#include "rnd.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#if defined(__FreeBSD__)
+#include <sys/types.h>
+#include <sys/lockmgr.h>
+#include <sys/sockio.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <sys/device.h>
+#endif
+
+#if defined(NRND) && NRND > 0
+#include <sys/rnd.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/if_types.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#if defined(__NetBSD__)
+#ifndef BPF_MTAP
+#define BPF_MTAP(_ifp, _m) do { \
+ if ((_ifp)->if_bpf)) { \
+ bpf_mtap((_ifp)->if_bpf, (_m)) ; \
+ } \
+} while (0)
+#endif
+#endif
+
+#if defined(__NetBSD__)
+#include <net/if_ether.h>
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_inarp.h>
+#endif /* INET */
+#elif defined(__FreeBSD__) /* defined(__NetBSD__) */
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif /* defined(__FreeBSD__) */
+
+#if defined(__NetBSD__)
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+#endif /* defined (__NetBSD__) */
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/usb_port.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/usb/if_udavreg.h>
+
+#if defined(__FreeBSD__)
+MODULE_DEPEND(udav, usb, 1, 1, 1);
+MODULE_DEPEND(udav, ether, 1, 1, 1);
+MODULE_DEPEND(udav, miibus, 1, 1, 1);
+#endif
+
+/* "device miibus" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+#if !defined(__FreeBSD__)
+/* Function declarations */
+USB_DECLARE_DRIVER(udav);
+#endif
+
+#if defined(__FreeBSD__)
+static device_probe_t udav_match;
+static device_attach_t udav_attach;
+static device_detach_t udav_detach;
+static device_shutdown_t udav_shutdown;
+static miibus_readreg_t udav_miibus_readreg;
+static miibus_writereg_t udav_miibus_writereg;
+static miibus_statchg_t udav_miibus_statchg;
+#endif
+
+static int udav_openpipes(struct udav_softc *);
+static void udav_start(struct ifnet *);
+static int udav_send(struct udav_softc *, struct mbuf *, int);
+static void udav_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+#if defined(__FreeBSD__)
+static void udav_rxstart(struct ifnet *ifp);
+#endif
+static void udav_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void udav_tick(void *);
+static void udav_tick_task(void *);
+static int udav_ioctl(struct ifnet *, u_long, caddr_t);
+static void udav_stop_task(struct udav_softc *);
+static void udav_stop(struct ifnet *, int);
+static void udav_watchdog(struct ifnet *);
+static int udav_ifmedia_change(struct ifnet *);
+static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *);
+static void udav_lock_mii(struct udav_softc *);
+static void udav_unlock_mii(struct udav_softc *);
+#if defined(__NetBSD__)
+static int udav_miibus_readreg(device_t, int, int);
+static void udav_miibus_writereg(device_t, int, int, int);
+static void udav_miibus_statchg(device_t);
+static int udav_init(struct ifnet *);
+#elif defined(__FreeBSD__)
+static void udav_init(void *);
+#endif
+static void udav_setmulti(struct udav_softc *);
+static void udav_reset(struct udav_softc *);
+
+static int udav_csr_read(struct udav_softc *, int, void *, int);
+static int udav_csr_write(struct udav_softc *, int, void *, int);
+static int udav_csr_read1(struct udav_softc *, int);
+static int udav_csr_write1(struct udav_softc *, int, unsigned char);
+
+#if 0
+static int udav_mem_read(struct udav_softc *, int, void *, int);
+static int udav_mem_write(struct udav_softc *, int, void *, int);
+static int udav_mem_write1(struct udav_softc *, int, unsigned char);
+#endif
+
+#if defined(__FreeBSD__)
+static device_method_t udav_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, udav_match),
+ DEVMETHOD(device_attach, udav_attach),
+ DEVMETHOD(device_detach, udav_detach),
+ DEVMETHOD(device_shutdown, udav_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, udav_miibus_readreg),
+ DEVMETHOD(miibus_writereg, udav_miibus_writereg),
+ DEVMETHOD(miibus_statchg, udav_miibus_statchg),
+
+ { 0, 0 }
+};
+
+static driver_t udav_driver = {
+ "udav",
+ udav_methods,
+ sizeof(struct udav_softc)
+};
+
+static devclass_t udav_devclass;
+
+DRIVER_MODULE(udav, uhub, udav_driver, udav_devclass, usbd_driver_load, 0);
+DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0);
+
+#endif /* defined(__FreeBSD__) */
+
+/* Macros */
+#ifdef UDAV_DEBUG
+#define DPRINTF(x) if (udavdebug) printf x
+#define DPRINTFN(n,x) if (udavdebug >= (n)) printf x
+int udavdebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define delay(d) DELAY(d)
+
+#define UDAV_SETBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x))
+
+#define UDAV_CLRBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x))
+
+static const struct udav_type {
+ struct usb_devno udav_dev;
+ u_int16_t udav_flags;
+#define UDAV_EXT_PHY 0x0001
+} udav_devs [] = {
+ /* Corega USB-TXC */
+ {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC }, 0},
+ /* ShanTou ST268 USB NIC */
+ {{ USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268 }, 0},
+ /* ShanTou DM9601 USB NIC */
+ {{ USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601}, 0},
+};
+#define udav_lookup(v, p) ((const struct udav_type *)usb_lookup(udav_devs, v, p))
+
+
+/* Probe */
+static int
+udav_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ return (udav_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+/* Attach */
+static int
+udav_attach(device_t self)
+{
+ USB_ATTACH_START(udav, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ const char *devname ;
+ struct ifnet *ifp;
+#if defined(__NetBSD__)
+ struct mii_data *mii;
+#endif
+ u_char eaddr[ETHER_ADDR_LEN];
+ int i;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ sc->sc_dev = self;
+ devname = device_get_nameunit(self);
+ /* Move the device into the configured state. */
+ err = usbd_set_config_no(dev, UDAV_CONFIG_NO, 1);
+ if (err) {
+ printf("%s: setting config no failed\n", devname);
+ goto bad;
+ }
+
+ usb_init_task(&sc->sc_tick_task, udav_tick_task, sc);
+ lockinit(&sc->sc_mii_lock, PZERO, "udavmii", 0, 0);
+ usb_init_task(&sc->sc_stop_task, (void (*)(void *)) udav_stop_task, sc);
+
+ /* get control interface */
+ err = usbd_device2interface_handle(dev, UDAV_IFACE_INDEX, &iface);
+ if (err) {
+ printf("%s: failed to get interface, err=%s\n", devname,
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ sc->sc_udev = dev;
+ sc->sc_ctl_iface = iface;
+ sc->sc_flags = udav_lookup(uaa->vendor, uaa->product)->udav_flags;
+
+ /* get interface descriptor */
+ id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
+
+ /* find endpoints */
+ sc->sc_bulkin_no = sc->sc_bulkout_no = sc->sc_intrin_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
+ if (ed == NULL) {
+ printf("%s: couldn't get endpoint %d\n", devname, i);
+ goto bad;
+ }
+ if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ sc->sc_bulkin_no = ed->bEndpointAddress; /* RX */
+ else if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
+ sc->sc_bulkout_no = ed->bEndpointAddress; /* TX */
+ else if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ sc->sc_intrin_no = ed->bEndpointAddress; /* Status */
+ }
+
+ if (sc->sc_bulkin_no == -1 || sc->sc_bulkout_no == -1 ||
+ sc->sc_intrin_no == -1) {
+ printf("%s: missing endpoint\n", devname);
+ goto bad;
+ }
+
+#if defined(__FreeBSD__)
+ mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+#endif
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ /* reset the adapter */
+ udav_reset(sc);
+
+ /* Get Ethernet Address */
+ err = udav_csr_read(sc, UDAV_PAR, (void *)eaddr, ETHER_ADDR_LEN);
+ if (err) {
+ printf("%s: read MAC address failed\n", devname);
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+ mtx_destroy(&sc->sc_mtx);
+#endif
+ goto bad;
+ }
+
+ /* Print Ethernet Address */
+ printf("%s: Ethernet address %s\n", devname, ether_sprintf(eaddr));
+
+ /* initialize interface infomation */
+#if defined(__FreeBSD__)
+ ifp = GET_IFP(sc) = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ printf("%s: can not if_alloc\n", devname);
+ UDAV_UNLOCK(sc);
+ mtx_destroy(&sc->sc_mtx);
+ goto bad;
+ }
+#else
+ ifp = GET_IFP(sc);
+#endif
+ ifp->if_softc = sc;
+ ifp->if_mtu = ETHERMTU;
+#if defined(__NetBSD__)
+ strncpy(ifp->if_xname, devname, IFNAMSIZ);
+#elif defined(__FreeBSD__)
+ if_initname(ifp, "udav", device_get_unit(self));
+#endif
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT;
+ ifp->if_start = udav_start;
+ ifp->if_ioctl = udav_ioctl;
+ ifp->if_watchdog = udav_watchdog;
+ ifp->if_init = udav_init;
+#if defined(__NetBSD__)
+ ifp->if_stop = udav_stop;
+#endif
+#if defined(__FreeBSD__)
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+#endif
+#if defined(__NetBSD__)
+ IFQ_SET_READY(&ifp->if_snd);
+#endif
+
+
+#if defined(__NetBSD__)
+ /*
+ * Do ifmedia setup.
+ */
+ mii = &sc->sc_mii;
+ mii->mii_ifp = ifp;
+ mii->mii_readreg = udav_miibus_readreg;
+ mii->mii_writereg = udav_miibus_writereg;
+ mii->mii_statchg = udav_miibus_statchg;
+ mii->mii_flags = MIIF_AUTOTSLEEP;
+ ifmedia_init(&mii->mii_media, 0,
+ udav_ifmedia_change, udav_ifmedia_status);
+ mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+ if (LIST_FIRST(&mii->mii_phys) == NULL) {
+ ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
+ ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
+ } else
+ ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
+
+ /* attach the interface */
+ if_attach(ifp);
+ Ether_ifattach(ifp, eaddr);
+#elif defined(__FreeBSD__)
+ if (mii_phy_probe(self, &sc->sc_miibus,
+ udav_ifmedia_change, udav_ifmedia_status)) {
+ printf("%s: MII without any PHY!\n", device_get_nameunit(sc->sc_dev));
+ if_free(ifp);
+ UDAV_UNLOCK(sc);
+ mtx_destroy(&sc->sc_mtx);
+ return ENXIO;
+ }
+
+ sc->sc_qdat.ifp = ifp;
+ sc->sc_qdat.if_rxstart = udav_rxstart;
+
+ /*
+ * Call MI attach routine.
+ */
+
+ ether_ifattach(ifp, eaddr);
+#endif
+
+#if defined(NRND) && NRND > 0
+ rnd_attach_source(&sc->rnd_source, devname, RND_TYPE_NET, 0);
+#endif
+
+ usb_callout_init(sc->sc_stat_ch);
+#if defined(__FreeBSD__)
+ usb_register_netisr();
+#endif
+ sc->sc_attached = 1;
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, sc->sc_dev);
+
+ return 0;
+
+ bad:
+ sc->sc_dying = 1;
+ return ENXIO;
+}
+
+/* detach */
+static int
+udav_detach(device_t self)
+{
+ USB_DETACH_START(udav, sc);
+ struct ifnet *ifp = GET_IFP(sc);
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ /* Detached before attached finished */
+ if (!sc->sc_attached)
+ return (0);
+
+ UDAV_LOCK(sc);
+
+ usb_uncallout(sc->sc_stat_ch, udav_tick, sc);
+
+ /* Remove any pending tasks */
+ usb_rem_task(sc->sc_udev, &sc->sc_tick_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_stop_task);
+
+#if defined(__NetBSD__)
+ s = splusb();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away */
+ usb_detach_wait(sc->sc_dev);
+ }
+#if defined(__FreeBSD__)
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+#else
+ if (ifp->if_flags & IFF_RUNNING)
+#endif
+ udav_stop(GET_IFP(sc), 1);
+
+#if defined(NRND) && NRND > 0
+ rnd_detach_source(&sc->rnd_source);
+#endif
+#if defined(__NetBSD__)
+ mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
+ ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
+#endif
+ ether_ifdetach(ifp);
+#if defined(__NetBSD__)
+ if_detach(ifp);
+#endif
+#if defined(__FreeBSD__)
+ if_free(ifp);
+#endif
+
+#ifdef DIAGNOSTIC
+ if (sc->sc_pipe_tx != NULL)
+ printf("%s: detach has active tx endpoint.\n",
+ device_get_nameunit(sc->sc_dev));
+ if (sc->sc_pipe_rx != NULL)
+ printf("%s: detach has active rx endpoint.\n",
+ device_get_nameunit(sc->sc_dev));
+ if (sc->sc_pipe_intr != NULL)
+ printf("%s: detach has active intr endpoint.\n",
+ device_get_nameunit(sc->sc_dev));
+#endif
+ sc->sc_attached = 0;
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+#if defined(__FreeBSD__)
+ mtx_destroy(&sc->sc_mtx);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+ return (0);
+}
+
+#if 0
+/* read memory */
+static int
+udav_mem_read(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xffff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ if (err) {
+ DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n",
+ device_get_nameunit(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+/* write memory */
+static int
+udav_mem_write(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xffff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ device_get_nameunit(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+/* write memory */
+static int
+udav_mem_write1(struct udav_softc *sc, int offset, unsigned char ch)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xffff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ device_get_nameunit(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+#endif
+
+/* read register(s) */
+static int
+udav_csr_read(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ if (err) {
+ DPRINTF(("%s: %s: read failed. off=%04x, err=%d\n",
+ device_get_nameunit(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+/* write register(s) */
+static int
+udav_csr_write(struct udav_softc *sc, int offset, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, buf);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ device_get_nameunit(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+static int
+udav_csr_read1(struct udav_softc *sc, int offset)
+{
+ u_int8_t val = 0;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ return (udav_csr_read(sc, offset, &val, 1) ? 0 : val);
+}
+
+/* write a register */
+static int
+udav_csr_write1(struct udav_softc *sc, int offset, unsigned char ch)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc == NULL)
+ return (0);
+
+ DPRINTFN(0x200,
+ ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ offset &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ sc->sc_refcnt++;
+ err = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ if (err) {
+ DPRINTF(("%s: %s: write failed. off=%04x, err=%d\n",
+ device_get_nameunit(sc->sc_dev), __func__, offset, err));
+ }
+
+ return (err);
+}
+
+#if defined(__NetBSD__)
+static int
+udav_init(struct ifnet *ifp)
+#elif defined(__FreeBSD__)
+static void
+udav_init(void *xsc)
+#endif
+{
+#if defined(__NetBSD__)
+ struct udav_softc *sc = ifp->if_softc;
+#elif defined(__FreeBSD__)
+ struct udav_softc *sc = (struct udav_softc *)xsc;
+ struct ifnet *ifp = GET_IFP(sc);
+#endif
+ struct mii_data *mii = GET_MII(sc);
+ u_char *eaddr;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+#if defined(__NetBSD__)
+ return (EIO);
+#elif defined(__FreeBSD__)
+ return ;
+#endif
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ /* Cancel pending I/O and free all TX/RX buffers */
+ udav_stop(ifp, 1);
+
+#if defined(__NetBSD__)
+ eaddr = LLADDR(ifp->if_sadl);
+#elif defined(__FreeBSD__)
+ eaddr = IF_LLADDR(ifp);
+#endif
+ udav_csr_write(sc, UDAV_PAR, eaddr, ETHER_ADDR_LEN);
+
+ /* Initialize network control register */
+ /* Disable loopback */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1);
+
+ /* Initialize RX control register */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
+
+ /* If we want promiscuous mode, accept all physical frames. */
+ if (ifp->if_flags & IFF_PROMISC)
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ else
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+
+ /* Initialize transmit ring */
+ if (usb_ether_tx_list_init(sc, &sc->sc_cdata,
+ sc->sc_udev) == ENOBUFS) {
+ printf("%s: tx list init failed\n", device_get_nameunit(sc->sc_dev));
+#if defined(__NetBSD__)
+ splx(s);
+ return (EIO);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+
+ }
+
+ /* Initialize receive ring */
+ if (usb_ether_rx_list_init(sc, &sc->sc_cdata,
+ sc->sc_udev) == ENOBUFS) {
+ printf("%s: rx list init failed\n", device_get_nameunit(sc->sc_dev));
+#if defined(__NetBSD__)
+ splx(s);
+ return (EIO);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+ }
+
+ /* Load the multicast filter */
+ udav_setmulti(sc);
+
+ /* Enable RX */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN);
+
+ /* clear POWER_DOWN state of internal PHY */
+ UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0);
+ UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0);
+
+ mii_mediachg(mii);
+
+ if (sc->sc_pipe_tx == NULL || sc->sc_pipe_rx == NULL) {
+ if (udav_openpipes(sc)) {
+#if defined(__NetBSD__)
+ splx(s);
+ return (EIO);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+ }
+ }
+
+#if defined(__FreeBSD__)
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+#else
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+#endif
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+ usb_callout(sc->sc_stat_ch, hz, udav_tick, sc);
+
+#if defined(__NetBSD__)
+ return (0);
+#elif defined(__FreeBSD__)
+ return ;
+#endif
+}
+
+static void
+udav_reset(struct udav_softc *sc)
+{
+ int i;
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ /* Select PHY */
+#if 1
+ /*
+ * XXX: force select internal phy.
+ * external phy routines are not tested.
+ */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+#else
+ if (sc->sc_flags & UDAV_EXT_PHY) {
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+ } else {
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+ }
+#endif
+
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST);
+
+ for (i = 0; i < UDAV_TX_TIMEOUT; i++) {
+ if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST))
+ break;
+ delay(10); /* XXX */
+ }
+ delay(10000); /* XXX */
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+udav_activate(device_t self, enum devact act)
+{
+ struct udav_softc *sc = (struct udav_softc *)self;
+
+ DPRINTF(("%s: %s: enter, act=%d\n", device_get_nameunit(sc->sc_dev),
+ __func__, act));
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ if_deactivate(&sc->sc_ec.ec_if);
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+#endif
+
+#define UDAV_BITS 6
+
+#define UDAV_CALCHASH(addr) \
+ (ether_crc32_le((addr), ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1))
+
+static void
+udav_setmulti(struct udav_softc *sc)
+{
+ struct ifnet *ifp;
+#if defined(__NetBSD__)
+ struct ether_multi *enm;
+ struct ether_multistep step;
+#elif defined(__FreeBSD__)
+ struct ifmultiaddr *ifma;
+#endif
+ u_int8_t hashes[8];
+ int h = 0;
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ ifp = GET_IFP(sc);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ return;
+ } else if (ifp->if_flags & IFF_ALLMULTI) {
+#if defined(__NetBSD__)
+ allmulti:
+#endif
+ ifp->if_flags |= IFF_ALLMULTI;
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_PRMSC);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ memset(hashes, 0x00, sizeof(hashes));
+ hashes[7] |= 0x80; /* broadcast address */
+ udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes));
+
+ /* now program new ones */
+#if defined(__NetBSD__)
+ ETHER_FIRST_MULTI(step, &sc->sc_ec, enm);
+ while (enm != NULL) {
+ if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
+ ETHER_ADDR_LEN) != 0)
+ goto allmulti;
+
+ h = UDAV_CALCHASH(enm->enm_addrlo);
+ hashes[h>>3] |= 1 << (h & 0x7);
+ ETHER_NEXT_MULTI(step, enm);
+ }
+#elif defined(__FreeBSD__)
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = UDAV_CALCHASH(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr));
+ hashes[h>>3] |= 1 << (h & 0x7);
+ }
+ IF_ADDR_UNLOCK(ifp);
+#endif
+
+ /* disable all multicast */
+ ifp->if_flags &= ~IFF_ALLMULTI;
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
+
+ /* write hash value to the register */
+ udav_csr_write(sc, UDAV_MAR, hashes, sizeof(hashes));
+}
+
+static int
+udav_openpipes(struct udav_softc *sc)
+{
+ struct ue_chain *c;
+ usbd_status err;
+ int i;
+ int error = 0;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ sc->sc_refcnt++;
+
+ /* Open RX pipe */
+ err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkin_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_pipe_rx);
+ if (err) {
+ printf("%s: open rx pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ error = EIO;
+ goto done;
+ }
+
+ /* Open TX pipe */
+ err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_pipe_tx);
+ if (err) {
+ printf("%s: open tx pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ error = EIO;
+ goto done;
+ }
+
+#if 0
+ /* XXX: interrupt endpoint is not yet supported */
+ /* Open Interrupt pipe */
+ err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_intrin_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_pipe_intr, sc,
+ &sc->sc_cdata.ue_ibuf, UDAV_INTR_PKGLEN,
+ udav_intr, UDAV_INTR_INTERVAL);
+ if (err) {
+ printf("%s: open intr pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ error = EIO;
+ goto done;
+ }
+#endif
+
+
+ /* Start up the receive pipe. */
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ c = &sc->sc_cdata.ue_rx_chain[i];
+ usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_rx,
+ c, c->ue_buf, UE_BUFSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, udav_rxeof);
+ (void)usbd_transfer(c->ue_xfer);
+ DPRINTF(("%s: %s: start read\n", device_get_nameunit(sc->sc_dev),
+ __func__));
+ }
+
+ done:
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+
+ return (error);
+}
+
+static void
+udav_start(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mbuf *m_head = NULL;
+
+ DPRINTF(("%s: %s: enter, link=%d\n", device_get_nameunit(sc->sc_dev),
+ __func__, sc->sc_link));
+
+ if (sc->sc_dying)
+ return;
+
+ if (!sc->sc_link)
+ return;
+
+#if defined(__FreeBSD__)
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
+#else
+ if (ifp->if_flags & IFF_OACTIVE)
+#endif
+ return;
+#if defined(__NetBSD__)
+ IFQ_POLL(&ifp->if_snd, m_head);
+#elif defined(__FreeBSD__)
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+#endif
+ if (m_head == NULL)
+ return;
+
+ if (udav_send(sc, m_head, 0)) {
+#if defined(__FreeBSD__)
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+#else
+ ifp->if_flags |= IFF_OACTIVE;
+#endif
+ return;
+ }
+
+#if defined(__NetBSD__)
+ IFQ_DEQUEUE(&ifp->if_snd, m_head);
+#endif
+
+#if NBPFILTER > 0
+ BPF_MTAP(ifp, m_head);
+#endif
+
+#if defined(__FreeBSD__)
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+#else
+ ifp->if_flags |= IFF_OACTIVE;
+#endif
+
+ /* Set a timeout in case the chip goes out to lunch. */
+ ifp->if_timer = 5;
+}
+
+static int
+udav_send(struct udav_softc *sc, struct mbuf *m, int idx)
+{
+ int total_len;
+ struct ue_chain *c;
+ usbd_status err;
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),__func__));
+
+ c = &sc->sc_cdata.ue_tx_chain[idx];
+
+ /* Copy the mbuf data into a contiguous buffer */
+ /* first 2 bytes are packet length */
+ m_copydata(m, 0, m->m_pkthdr.len, c->ue_buf + 2);
+ c->ue_mbuf = m;
+ total_len = m->m_pkthdr.len;
+ if (total_len < UDAV_MIN_FRAME_LEN) {
+ memset(c->ue_buf + 2 + total_len, 0,
+ UDAV_MIN_FRAME_LEN - total_len);
+ total_len = UDAV_MIN_FRAME_LEN;
+ }
+
+ /* Frame length is specified in the first 2bytes of the buffer */
+ c->ue_buf[0] = (u_int8_t)total_len;
+ c->ue_buf[1] = (u_int8_t)(total_len >> 8);
+ total_len += 2;
+
+ usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_tx, c, c->ue_buf, total_len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
+ UDAV_TX_TIMEOUT, udav_txeof);
+
+ /* Transmit */
+ sc->sc_refcnt++;
+ err = usbd_transfer(c->ue_xfer);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ if (err != USBD_IN_PROGRESS) {
+ printf("%s: udav_send error=%s\n", device_get_nameunit(sc->sc_dev),
+ usbd_errstr(err));
+ /* Stop the interface */
+ usb_add_task(sc->sc_udev, &sc->sc_stop_task, USB_TASKQ_DRIVER);
+ return (EIO);
+ }
+
+ DPRINTF(("%s: %s: send %d bytes\n", device_get_nameunit(sc->sc_dev),
+ __func__, total_len));
+
+ sc->sc_cdata.ue_tx_cnt++;
+
+ return (0);
+}
+
+static void
+udav_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ struct udav_softc *sc = c->ue_sc;
+ struct ifnet *ifp = GET_IFP(sc);
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ if (sc->sc_dying)
+ return;
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ ifp->if_timer = 0;
+#if defined(__FreeBSD__)
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+#else
+ ifp->if_flags &= ~IFF_OACTIVE;
+#endif
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+ return;
+ }
+ ifp->if_oerrors++;
+ printf("%s: usb error on tx: %s\n", device_get_nameunit(sc->sc_dev),
+ usbd_errstr(status));
+ if (status == USBD_STALLED) {
+ sc->sc_refcnt++;
+ usbd_clear_endpoint_stall(sc->sc_pipe_tx);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ }
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+ return;
+ }
+
+ ifp->if_opackets++;
+
+ m_freem(c->ue_mbuf);
+ c->ue_mbuf = NULL;
+
+#if defined(__NetBSD__)
+ if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+#elif defined(__FreeBSD__)
+ if ( ifp->if_snd.ifq_head != NULL )
+#endif
+ udav_start(ifp);
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+}
+
+static void
+udav_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ue_chain *c = priv;
+ struct udav_softc *sc = c->ue_sc;
+ struct ifnet *ifp = GET_IFP(sc);
+ struct mbuf *m;
+ u_int32_t total_len;
+ u_int8_t *pktstat;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),__func__));
+
+ if (sc->sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ sc->sc_rx_errs++;
+ if (usbd_ratecheck(&sc->sc_rx_notice)) {
+ printf("%s: %u usb errors on rx: %s\n",
+ device_get_nameunit(sc->sc_dev), sc->sc_rx_errs,
+ usbd_errstr(status));
+ sc->sc_rx_errs = 0;
+ }
+ if (status == USBD_STALLED) {
+ sc->sc_refcnt++;
+ usbd_clear_endpoint_stall(sc->sc_pipe_rx);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ }
+ goto done;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+
+ /* copy data to mbuf */
+ m = c->ue_mbuf;
+ memcpy(mtod(m, char *), c->ue_buf, total_len);
+
+ /* first byte in received data */
+ pktstat = mtod(m, u_int8_t *);
+ m_adj(m, sizeof(u_int8_t));
+ DPRINTF(("%s: RX Status: 0x%02x\n", device_get_nameunit(sc->sc_dev), *pktstat));
+
+ total_len = UGETW(mtod(m, u_int8_t *));
+ m_adj(m, sizeof(u_int16_t));
+
+ if (*pktstat & UDAV_RSR_LCS) {
+ ifp->if_collisions++;
+ goto done;
+ }
+
+ if (total_len < sizeof(struct ether_header) ||
+ *pktstat & UDAV_RSR_ERR) {
+ ifp->if_ierrors++;
+ goto done;
+ }
+
+ ifp->if_ipackets++;
+ total_len -= ETHER_CRC_LEN;
+
+ m->m_pkthdr.len = m->m_len = total_len;
+#if defined(__NetBSD__)
+ m->m_pkthdr.rcvif = ifp;
+#elif defined(__FreeBSD__)
+ m->m_pkthdr.rcvif = (struct ifnet *)&sc->sc_qdat;
+#endif
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+#if defined(__NetBSD__)
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL) {
+ printf("%s: no memory for rx list "
+ "-- packet dropped!\n", device_get_nameunit(sc->sc_dev));
+ ifp->if_ierrors++;
+ goto done1;
+ }
+#endif
+
+#if NBPFILTER > 0
+ BPF_MTAP(ifp, m);
+#endif
+
+ DPRINTF(("%s: %s: deliver %d\n", device_get_nameunit(sc->sc_dev),
+ __func__, m->m_len));
+#if defined(__NetBSD__)
+ IF_INPUT(ifp, m);
+#endif
+#if defined(__FreeBSD__)
+ usb_ether_input(m);
+ UDAV_UNLOCK(sc);
+ return ;
+#endif
+
+#if defined(__NetBSD__)
+ done1:
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+ done:
+ /* Setup new transfer */
+ usbd_setup_xfer(xfer, sc->sc_pipe_rx, c, c->ue_buf, UE_BUFSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, udav_rxeof);
+ sc->sc_refcnt++;
+ usbd_transfer(xfer);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+
+ DPRINTF(("%s: %s: start rx\n", device_get_nameunit(sc->sc_dev), __func__));
+}
+
+#if 0
+static void udav_intr()
+{
+}
+#endif
+
+static int
+udav_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+#if defined(__NetBSD__)
+ int s;
+#endif
+ int error = 0;
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ switch (cmd) {
+#if defined(__FreeBSD__)
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ ifp->if_flags & IFF_PROMISC) {
+ UDAV_SETBIT(sc, UDAV_RCR,
+ UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ !(ifp->if_flags & IFF_PROMISC)) {
+ if (ifp->if_flags & IFF_ALLMULTI)
+ UDAV_CLRBIT(sc, UDAV_RCR,
+ UDAV_RCR_PRMSC);
+ else
+ UDAV_CLRBIT(sc, UDAV_RCR,
+ UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ udav_init(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ udav_stop(ifp, 1);
+ }
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ udav_setmulti(sc);
+ error = 0;
+ break;
+#endif
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = GET_MII(sc);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+#if defined(__NetBSD__)
+ if (error == ENETRESET) {
+ udav_setmulti(sc);
+ error = 0;
+ }
+#endif
+ break;
+ }
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+
+ return (error);
+}
+
+static void
+udav_watchdog(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct ue_chain *c;
+ usbd_status stat;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ ifp->if_oerrors++;
+ printf("%s: watchdog timeout\n", device_get_nameunit(sc->sc_dev));
+
+#if defined(__NetBSD__)
+ s = splusb();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc)
+#endif
+ c = &sc->sc_cdata.ue_tx_chain[0];
+ usbd_get_xfer_status(c->ue_xfer, NULL, NULL, NULL, &stat);
+ udav_txeof(c->ue_xfer, c, stat);
+
+#if defined(__NetBSD__)
+ if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+#elif defined(__FreeBSD__)
+ if ( ifp->if_snd.ifq_head != NULL )
+#endif
+ udav_start(ifp);
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+}
+
+static void
+udav_stop_task(struct udav_softc *sc)
+{
+ udav_stop(GET_IFP(sc), 1);
+}
+
+/* Stop the adapter and free any mbufs allocated to the RX and TX lists. */
+static void
+udav_stop(struct ifnet *ifp, int disable)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ usbd_status err;
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ ifp->if_timer = 0;
+
+ udav_reset(sc);
+
+ usb_uncallout(sc->sc_stat_ch, udav_tick, sc);
+
+ /* Stop transfers */
+ /* RX endpoint */
+ if (sc->sc_pipe_rx != NULL) {
+ err = usbd_abort_pipe(sc->sc_pipe_rx);
+ if (err)
+ printf("%s: abort rx pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_pipe_rx);
+ if (err)
+ printf("%s: close rx pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ sc->sc_pipe_rx = NULL;
+ }
+
+ /* TX endpoint */
+ if (sc->sc_pipe_tx != NULL) {
+ err = usbd_abort_pipe(sc->sc_pipe_tx);
+ if (err)
+ printf("%s: abort tx pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_pipe_tx);
+ if (err)
+ printf("%s: close tx pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ sc->sc_pipe_tx = NULL;
+ }
+
+#if 0
+ /* XXX: Interrupt endpoint is not yet supported!! */
+ /* Interrupt endpoint */
+ if (sc->sc_pipe_intr != NULL) {
+ err = usbd_abort_pipe(sc->sc_pipe_intr);
+ if (err)
+ printf("%s: abort intr pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_pipe_intr);
+ if (err)
+ printf("%s: close intr pipe failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ sc->sc_pipe_intr = NULL;
+ }
+#endif
+
+ /* Free RX resources. */
+ usb_ether_rx_list_free(&sc->sc_cdata);
+ /* Free TX resources. */
+ usb_ether_tx_list_free(&sc->sc_cdata);
+
+ sc->sc_link = 0;
+#if defined(__FreeBSD__)
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+#else
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+#endif
+}
+
+/* Set media options */
+static int
+udav_ifmedia_change(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return (0);
+
+ sc->sc_link = 0;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+ for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
+ miisc = LIST_NEXT(miisc, mii_list))
+ mii_phy_reset(miisc);
+ }
+
+ return (mii_mediachg(mii));
+}
+
+/* Report current media status. */
+static void
+udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+
+ if (sc->sc_dying)
+ return;
+
+#if defined(__FreeBSD__)
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+#else
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+#endif
+ ifmr->ifm_active = IFM_ETHER | IFM_NONE;
+ ifmr->ifm_status = 0;
+ return;
+ }
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+udav_tick(void *xsc)
+{
+ struct udav_softc *sc = xsc;
+
+ if (sc == NULL)
+ return;
+
+ DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
+ __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ /* Perform periodic stuff in process context */
+ usb_add_task(sc->sc_udev, &sc->sc_tick_task, USB_TASKQ_DRIVER);
+}
+
+static void
+udav_tick_task(void *xsc)
+{
+ struct udav_softc *sc = xsc;
+ struct ifnet *ifp;
+ struct mii_data *mii;
+#if defined(__NetBSD__)
+ int s;
+#endif
+
+ if (sc == NULL)
+ return;
+
+ DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
+ __func__));
+
+ if (sc->sc_dying)
+ return;
+
+ ifp = GET_IFP(sc);
+ mii = GET_MII(sc);
+
+ if (mii == NULL)
+ return;
+
+#if defined(__NetBSD__)
+ s = splnet();
+#elif defined(__FreeBSD__)
+ UDAV_LOCK(sc);
+#endif
+
+ mii_tick(mii);
+ if (!sc->sc_link) {
+ mii_pollstat(mii);
+ if (mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ DPRINTF(("%s: %s: got link\n",
+ device_get_nameunit(sc->sc_dev), __func__));
+ sc->sc_link++;
+#if defined(__NetBSD__)
+ if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+#elif defined(__FreeBSD__)
+ if ( ifp->if_snd.ifq_head != NULL )
+#endif
+ udav_start(ifp);
+ }
+ }
+
+ usb_callout(sc->sc_stat_ch, hz, udav_tick, sc);
+
+#if defined(__NetBSD__)
+ splx(s);
+#elif defined(__FreeBSD__)
+ UDAV_UNLOCK(sc);
+#endif
+}
+
+/* Get exclusive access to the MII registers */
+static void
+udav_lock_mii(struct udav_softc *sc)
+{
+ DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
+ __func__));
+
+ sc->sc_refcnt++;
+ lockmgr(&sc->sc_mii_lock, LK_EXCLUSIVE, NULL);
+}
+
+static void
+udav_unlock_mii(struct udav_softc *sc)
+{
+ DPRINTFN(0xff, ("%s: %s: enter\n", device_get_nameunit(sc->sc_dev),
+ __func__));
+
+ lockmgr(&sc->sc_mii_lock, LK_RELEASE, NULL);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+}
+
+static int
+udav_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct udav_softc *sc;
+ u_int8_t val[2];
+ u_int16_t data16;
+
+ if (dev == NULL)
+ return (0);
+
+ sc = USBGETSOFTC(dev);
+
+ DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x\n",
+ device_get_nameunit(sc->sc_dev), __func__, phy, reg));
+
+ if (sc->sc_dying) {
+#ifdef DIAGNOSTIC
+ printf("%s: %s: dying\n", device_get_nameunit(sc->sc_dev),
+ __func__);
+#endif
+ return (0);
+ }
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0) {
+ DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
+ device_get_nameunit(sc->sc_dev), __func__, phy));
+ return (0);
+ }
+
+ udav_lock_mii(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* select PHY operation and start read command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR);
+
+ /* XXX: should be wait? */
+
+ /* end read command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR);
+
+ /* retrieve the result from data registers */
+ udav_csr_read(sc, UDAV_EPDRL, val, 2);
+
+ udav_unlock_mii(sc);
+
+ data16 = val[0] | (val[1] << 8);
+
+ DPRINTFN(0xff, ("%s: %s: phy=%d reg=0x%04x => 0x%04x\n",
+ device_get_nameunit(sc->sc_dev), __func__, phy, reg, data16));
+
+ return (data16);
+}
+
+static int
+udav_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct udav_softc *sc;
+ u_int8_t val[2];
+
+ if (dev == NULL)
+ return (0); /* XXX real error? */
+
+ sc = USBGETSOFTC(dev);
+
+ DPRINTFN(0xff, ("%s: %s: enter, phy=%d reg=0x%04x data=0x%04x\n",
+ device_get_nameunit(sc->sc_dev), __func__, phy, reg, data));
+
+ if (sc->sc_dying) {
+#ifdef DIAGNOSTIC
+ printf("%s: %s: dying\n", device_get_nameunit(sc->sc_dev),
+ __func__);
+#endif
+ return (0); /* XXX real error? */
+ }
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0) {
+ DPRINTFN(0xff, ("%s: %s: phy=%d is not supported\n",
+ device_get_nameunit(sc->sc_dev), __func__, phy));
+ return (0); /* XXX real error? */
+ }
+
+ udav_lock_mii(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* put the value to the data registers */
+ val[0] = data & 0xff;
+ val[1] = (data >> 8) & 0xff;
+ udav_csr_write(sc, UDAV_EPDRL, val, 2);
+
+ /* select PHY operation and start write command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW);
+
+ /* XXX: should be wait? */
+
+ /* end write command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW);
+
+ udav_unlock_mii(sc);
+
+ return (0);
+}
+
+static void
+udav_miibus_statchg(device_t dev)
+{
+#ifdef UDAV_DEBUG
+ struct udav_softc *sc;
+
+ if (dev == NULL)
+ return;
+
+ sc = USBGETSOFTC(dev);
+ DPRINTF(("%s: %s: enter\n", device_get_nameunit(sc->sc_dev), __func__));
+#endif
+ /* Nothing to do */
+}
+
+#if defined(__FreeBSD__)
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+udav_shutdown(device_t dev)
+{
+ struct udav_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ udav_stop_task(sc);
+
+ return (0);
+}
+
+static void
+udav_rxstart(struct ifnet *ifp)
+{
+ struct udav_softc *sc;
+ struct ue_chain *c;
+
+ sc = ifp->if_softc;
+ UDAV_LOCK(sc);
+ c = &sc->sc_cdata.ue_rx_chain[sc->sc_cdata.ue_rx_prod];
+
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL) {
+ printf("%s: no memory for rx list "
+ "-- packet dropped!\n", device_get_nameunit(sc->sc_dev));
+ ifp->if_ierrors++;
+ UDAV_UNLOCK(sc);
+ return;
+ }
+
+ /* Setup new transfer. */
+ usbd_setup_xfer(c->ue_xfer, sc->sc_pipe_rx,
+ c, c->ue_buf, UE_BUFSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, udav_rxeof);
+ usbd_transfer(c->ue_xfer);
+
+ UDAV_UNLOCK(sc);
+ return;
+}
+#endif
diff --git a/sys/legacy/dev/usb/if_udavreg.h b/sys/legacy/dev/usb/if_udavreg.h
new file mode 100644
index 0000000..cb7da28
--- /dev/null
+++ b/sys/legacy/dev/usb/if_udavreg.h
@@ -0,0 +1,210 @@
+/* $NetBSD: if_udavreg.h,v 1.2 2003/09/04 15:17:39 tsutsui Exp $ */
+/* $nabe: if_udavreg.h,v 1.2 2003/08/21 16:26:40 nabe Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.org>. All rights reserved.
+ *
+ * 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. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define UDAV_IFACE_INDEX 0
+#define UDAV_CONFIG_NO 1
+
+#define UDAV_TX_TIMEOUT 1000
+#define UDAV_TIMEOUT 10000
+
+#define ETHER_ALIGN 2
+
+
+/* Packet length */
+#define UDAV_MIN_FRAME_LEN 60
+
+/* Request */
+#define UDAV_REQ_REG_READ 0x00 /* Read from register(s) */
+#define UDAV_REQ_REG_WRITE 0x01 /* Write to register(s) */
+#define UDAV_REQ_REG_WRITE1 0x03 /* Write to a register */
+
+#define UDAV_REQ_MEM_READ 0x02 /* Read from memory */
+#define UDAV_REQ_MEM_WRITE 0x05 /* Write to memory */
+#define UDAV_REQ_MEM_WRITE1 0x07 /* Write a byte to memory */
+
+/* Registers */
+#define UDAV_NCR 0x00 /* Network Control Register */
+#define UDAV_NCR_EXT_PHY (1<<7) /* Select External PHY */
+#define UDAV_NCR_WAKEEN (1<<6) /* Wakeup Event Enable */
+#define UDAV_NCR_FCOL (1<<4) /* Force Collision Mode */
+#define UDAV_NCR_FDX (1<<3) /* Full-Duplex Mode (RO on Int. PHY) */
+#define UDAV_NCR_LBK1 (1<<2) /* Lookback Mode */
+#define UDAV_NCR_LBK0 (1<<1) /* Lookback Mode */
+#define UDAV_NCR_RST (1<<0) /* Software reset */
+
+#define UDAV_RCR 0x05 /* RX Control Register */
+#define UDAV_RCR_WTDIS (1<<6) /* Watchdog Timer Disable */
+#define UDAV_RCR_DIS_LONG (1<<5) /* Discard Long Packet(over 1522Byte) */
+#define UDAV_RCR_DIS_CRC (1<<4) /* Discard CRC Error Packet */
+#define UDAV_RCR_ALL (1<<3) /* Pass All Multicast */
+#define UDAV_RCR_RUNT (1<<2) /* Pass Runt Packet */
+#define UDAV_RCR_PRMSC (1<<1) /* Promiscuous Mode */
+#define UDAV_RCR_RXEN (1<<0) /* RX Enable */
+
+#define UDAV_RSR 0x06 /* RX Status Register */
+#define UDAV_RSR_RF (1<<7) /* Runt Frame */
+#define UDAV_RSR_MF (1<<6) /* Multicast Frame */
+#define UDAV_RSR_LCS (1<<5) /* Late Collision Seen */
+#define UDAV_RSR_RWTO (1<<4) /* Receive Watchdog Time-Out */
+#define UDAV_RSR_PLE (1<<3) /* Physical Layer Error */
+#define UDAV_RSR_AE (1<<2) /* Alignment Error */
+#define UDAV_RSR_CE (1<<1) /* CRC Error */
+#define UDAV_RSR_FOE (1<<0) /* FIFO Overflow Error */
+#define UDAV_RSR_ERR (UDAV_RSR_RF | UDAV_RSR_LCS | UDAV_RSR_RWTO |\
+ UDAV_RSR_PLE | UDAV_RSR_AE | UDAV_RSR_CE |\
+ UDAV_RSR_FOE)
+
+#define UDAV_EPCR 0x0b /* EEPROM & PHY Control Register */
+#define UDAV_EPCR_REEP (1<<5) /* Reload EEPROM */
+#define UDAV_EPCR_WEP (1<<4) /* Write EEPROM enable */
+#define UDAV_EPCR_EPOS (1<<3) /* EEPROM or PHY Operation Select */
+#define UDAV_EPCR_ERPRR (1<<2) /* EEPROM/PHY Register Read Command */
+#define UDAV_EPCR_ERPRW (1<<1) /* EEPROM/PHY Register Write Command */
+#define UDAV_EPCR_ERRE (1<<0) /* EEPROM/PHY Access Status */
+
+#define UDAV_EPAR 0x0c /* EEPROM & PHY Control Register */
+#define UDAV_EPAR_PHY_ADR1 (1<<7) /* PHY Address bit 1 */
+#define UDAV_EPAR_PHY_ADR0 (1<<6) /* PHY Address bit 0 */
+#define UDAV_EPAR_EROA (1<<0) /* EEPROM Word/PHY Register Address */
+#define UDAV_EPAR_EROA_MASK (0x1f) /* [5:0] */
+
+#define UDAV_EPDRL 0x0d /* EEPROM & PHY Data Register */
+#define UDAV_EPDRH 0x0e /* EEPROM & PHY Data Register */
+
+#define UDAV_PAR0 0x10 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR1 0x11 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR2 0x12 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR3 0x13 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR4 0x14 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR5 0x15 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR UDAV_PAR0
+
+#define UDAV_MAR0 0x16 /* Multicast Register */
+#define UDAV_MAR1 0x17 /* Multicast Register */
+#define UDAV_MAR2 0x18 /* Multicast Register */
+#define UDAV_MAR3 0x19 /* Multicast Register */
+#define UDAV_MAR4 0x1a /* Multicast Register */
+#define UDAV_MAR5 0x1b /* Multicast Register */
+#define UDAV_MAR6 0x1c /* Multicast Register */
+#define UDAV_MAR7 0x1d /* Multicast Register */
+#define UDAV_MAR UDAV_MAR0
+
+#define UDAV_GPCR 0x1e /* General purpose control register */
+#define UDAV_GPCR_GEP_CNTL6 (1<<6) /* General purpose control 6 */
+#define UDAV_GPCR_GEP_CNTL5 (1<<5) /* General purpose control 5 */
+#define UDAV_GPCR_GEP_CNTL4 (1<<4) /* General purpose control 4 */
+#define UDAV_GPCR_GEP_CNTL3 (1<<3) /* General purpose control 3 */
+#define UDAV_GPCR_GEP_CNTL2 (1<<2) /* General purpose control 2 */
+#define UDAV_GPCR_GEP_CNTL1 (1<<1) /* General purpose control 1 */
+#define UDAV_GPCR_GEP_CNTL0 (1<<0) /* General purpose control 0 */
+
+#define UDAV_GPR 0x1f /* General purpose register */
+#define UDAV_GPR_GEPIO6 (1<<6) /* General purpose 6 */
+#define UDAV_GPR_GEPIO5 (1<<5) /* General purpose 5 */
+#define UDAV_GPR_GEPIO4 (1<<4) /* General purpose 4 */
+#define UDAV_GPR_GEPIO3 (1<<3) /* General purpose 3 */
+#define UDAV_GPR_GEPIO2 (1<<2) /* General purpose 2 */
+#define UDAV_GPR_GEPIO1 (1<<1) /* General purpose 1 */
+#define UDAV_GPR_GEPIO0 (1<<0) /* General purpose 0 */
+
+#if defined(__FreeBSD__)
+#define GET_IFP(sc) ((sc)->sc_ifp)
+#elif defined(__OpenBSD__)
+#define GET_IFP(sc) (&(sc)->sc_ac.ac_if)
+#elif defined(__NetBSD__)
+#define GET_IFP(sc) (&(sc)->sc_ec.ec_if)
+#endif
+#if defined(__FreeBSD__)
+#define GET_MII(sc) (device_get_softc((sc)->sc_miibus))
+#else
+#define GET_MII(sc) (&(sc)->sc_mii)
+#endif
+
+#if defined(__FreeBSD__)
+#if 0
+#define UDAV_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define UDAV_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#else
+#define UDAV_LOCK(_sc)
+#define UDAV_UNLOCK(_sc)
+#endif
+#endif
+
+struct udav_softc {
+#if defined(__FreeBSD__)
+ struct ifnet *sc_ifp;
+#endif
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+
+ /* USB */
+ usbd_interface_handle sc_ctl_iface;
+ /* int sc_ctl_iface_no; */
+ int sc_bulkin_no; /* bulk in endpoint */
+ int sc_bulkout_no; /* bulk out endpoint */
+ int sc_intrin_no; /* intr in endpoint */
+ usbd_pipe_handle sc_pipe_rx;
+ usbd_pipe_handle sc_pipe_tx;
+ usbd_pipe_handle sc_pipe_intr;
+ usb_callout_t sc_stat_ch;
+ u_int sc_rx_errs;
+ /* u_int sc_intr_errs; */
+ struct timeval sc_rx_notice;
+
+ /* Ethernet */
+
+#if defined(__FreeBSD__)
+ device_t sc_miibus ;
+ struct mtx sc_mtx ;
+ struct usb_qdat sc_qdat;
+#elif defined(__NetBSD__)
+ struct ethercom sc_ec; /* ethernet common */
+ struct mii_data sc_mii;
+#endif
+ struct lock sc_mii_lock;
+ int sc_link;
+#define sc_media udav_mii.mii_media
+#if defined(NRND) && NRND > 0
+ rndsource_element_t rnd_source;
+#endif
+ struct ue_cdata sc_cdata;
+
+ int sc_attached;
+ int sc_dying;
+ int sc_refcnt;
+
+ struct usb_task sc_tick_task;
+ struct usb_task sc_stop_task;
+
+ u_int16_t sc_flags;
+};
diff --git a/sys/legacy/dev/usb/if_upgt.c b/sys/legacy/dev/usb/if_upgt.c
new file mode 100644
index 0000000..9ab226f
--- /dev/null
+++ b/sys/legacy/dev/usb/if_upgt.c
@@ -0,0 +1,2375 @@
+/* $OpenBSD: if_upgt.c,v 1.35 2008/04/16 18:32:15 damien Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/firmware.h>
+#include <sys/linker.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_regdomain.h>
+
+#include <net/bpf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/usb/if_upgtvar.h>
+
+/*
+ * Driver for the USB PrismGT devices.
+ *
+ * For now just USB 2.0 devices with the GW3887 chipset are supported.
+ * The driver has been written based on the firmware version 2.13.1.0_LM87.
+ *
+ * TODO's:
+ * - MONITOR mode test.
+ * - Add HOSTAP mode.
+ * - Add IBSS mode.
+ * - Support the USB 1.0 devices (NET2280, ISL3880, ISL3886 chipsets).
+ *
+ * Parts of this driver has been influenced by reading the p54u driver
+ * written by Jean-Baptiste Note <jean-baptiste.note@m4x.org> and
+ * Sebastien Bourdeauducq <lekernel@prism54.org>.
+ */
+
+SYSCTL_NODE(_hw, OID_AUTO, upgt, CTLFLAG_RD, 0,
+ "USB PrismGT GW3887 driver parameters");
+
+/*
+ * NB: normally `upgt_txbuf' value can be increased to maximum 6, mininum 1.
+ * However, we're using just 2 txbufs to protect packet losses in some cases
+ * so the performance was sacrificed that with this value its speed is about
+ * 2.1Mb/s.
+ *
+ * With setting txbuf value as 6, you can get full speed, 3.0Mb/s, of this
+ * device but sometimes you'd meet some packet losses then retransmision.
+ */
+static int upgt_txbuf = UPGT_TX_COUNT; /* # tx buffers to allocate */
+SYSCTL_INT(_hw_upgt, OID_AUTO, txbuf, CTLFLAG_RW, &upgt_txbuf,
+ 0, "tx buffers allocated");
+TUNABLE_INT("hw.upgt.txbuf", &upgt_txbuf);
+
+#ifdef UPGT_DEBUG
+int upgt_debug = 0;
+SYSCTL_INT(_hw_upgt, OID_AUTO, debug, CTLFLAG_RW, &upgt_debug,
+ 0, "control debugging printfs");
+TUNABLE_INT("hw.upgt.debug", &upgt_debug);
+enum {
+ UPGT_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ UPGT_DEBUG_RECV = 0x00000002, /* basic recv operation */
+ UPGT_DEBUG_RESET = 0x00000004, /* reset processing */
+ UPGT_DEBUG_INTR = 0x00000008, /* INTR */
+ UPGT_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */
+ UPGT_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */
+ UPGT_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */
+ UPGT_DEBUG_STAT = 0x00000080, /* statistic */
+ UPGT_DEBUG_FW = 0x00000100, /* firmware */
+ UPGT_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (sc->sc_debug & (m)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+
+/*
+ * Prototypes.
+ */
+static device_probe_t upgt_match;
+static device_attach_t upgt_attach;
+static device_detach_t upgt_detach;
+static int upgt_alloc_tx(struct upgt_softc *);
+static int upgt_alloc_rx(struct upgt_softc *);
+static int upgt_alloc_cmd(struct upgt_softc *);
+static int upgt_attach_hook(device_t);
+static int upgt_device_reset(struct upgt_softc *);
+static int upgt_bulk_xmit(struct upgt_softc *, struct upgt_data *,
+ usbd_pipe_handle, uint32_t *, int);
+static int upgt_fw_verify(struct upgt_softc *);
+static int upgt_mem_init(struct upgt_softc *);
+static int upgt_fw_load(struct upgt_softc *);
+static int upgt_fw_copy(const uint8_t *, char *, int);
+static uint32_t upgt_crc32_le(const void *, size_t);
+static void upgt_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void upgt_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static int upgt_eeprom_read(struct upgt_softc *);
+static int upgt_eeprom_parse(struct upgt_softc *);
+static void upgt_eeprom_parse_hwrx(struct upgt_softc *, uint8_t *);
+static void upgt_eeprom_parse_freq3(struct upgt_softc *, uint8_t *, int);
+static void upgt_eeprom_parse_freq4(struct upgt_softc *, uint8_t *, int);
+static void upgt_eeprom_parse_freq6(struct upgt_softc *, uint8_t *, int);
+static uint32_t upgt_chksum_le(const uint32_t *, size_t);
+static void upgt_tx_done(struct upgt_softc *, uint8_t *);
+static void upgt_rx(struct upgt_softc *, uint8_t *, int);
+static void upgt_init(void *);
+static void upgt_init_locked(struct upgt_softc *);
+static int upgt_ioctl(struct ifnet *, u_long, caddr_t);
+static void upgt_start(struct ifnet *);
+static int upgt_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void upgt_scan_start(struct ieee80211com *);
+static void upgt_scan_end(struct ieee80211com *);
+static void upgt_set_channel(struct ieee80211com *);
+static struct ieee80211vap *upgt_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void upgt_vap_delete(struct ieee80211vap *);
+static void upgt_update_mcast(struct ifnet *);
+static uint8_t upgt_rx_rate(struct upgt_softc *, const int);
+static void upgt_set_multi(void *);
+static void upgt_stop(struct upgt_softc *, int);
+static void upgt_setup_rates(struct ieee80211vap *, struct ieee80211com *);
+static int upgt_set_macfilter(struct upgt_softc *, uint8_t);
+static int upgt_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static void upgt_task(void *);
+static void upgt_scantask(void *);
+static void upgt_set_chan(struct upgt_softc *, struct ieee80211_channel *);
+static void upgt_set_led(struct upgt_softc *, int);
+static void upgt_set_led_blink(void *);
+static void upgt_tx_task(void *);
+static int upgt_get_stats(struct upgt_softc *);
+static void upgt_mem_free(struct upgt_softc *, uint32_t);
+static uint32_t upgt_mem_alloc(struct upgt_softc *);
+static void upgt_free_tx(struct upgt_softc *);
+static void upgt_free_rx(struct upgt_softc *);
+static void upgt_free_cmd(struct upgt_softc *);
+static void upgt_watchdog(void *);
+
+static const char *upgt_fwname = "upgt-gw3887";
+
+static const struct usb_devno upgt_devs_2[] = {
+ /* version 2 devices */
+ { USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_PRISM_GT },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050 },
+ { USB_VENDOR_CONCEPTRONIC, USB_PRODUCT_CONCEPTRONIC_PRISM_GT },
+ { USB_VENDOR_DELL, USB_PRODUCT_DELL_PRISM_GT_1 },
+ { USB_VENDOR_DELL, USB_PRODUCT_DELL_PRISM_GT_2 },
+ { USB_VENDOR_FSC, USB_PRODUCT_FSC_E5400 },
+ { USB_VENDOR_GLOBESPAN, USB_PRODUCT_GLOBESPAN_PRISM_GT_1 },
+ { USB_VENDOR_GLOBESPAN, USB_PRODUCT_GLOBESPAN_PRISM_GT_2 },
+ { USB_VENDOR_INTERSIL, USB_PRODUCT_INTERSIL_PRISM_GT },
+ { USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG },
+ { USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR045G },
+ { USB_VENDOR_XYRATEX, USB_PRODUCT_XYRATEX_PRISM_GT_1 },
+ { USB_VENDOR_XYRATEX, USB_PRODUCT_XYRATEX_PRISM_GT_2 },
+ { USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_XG703A }
+};
+
+static int
+upgt_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (!uaa->iface)
+ return UMATCH_NONE;
+
+ if (usb_lookup(upgt_devs_2, uaa->vendor, uaa->product) != NULL)
+ return (UMATCH_VENDOR_PRODUCT);
+
+ return (UMATCH_NONE);
+}
+
+static int
+upgt_attach(device_t dev)
+{
+ int i;
+ struct upgt_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ usb_endpoint_descriptor_t *ed;
+ usb_interface_descriptor_t *id;
+ usbd_status error;
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+#ifdef UPGT_DEBUG
+ sc->sc_debug = upgt_debug;
+#endif
+
+ /* set configuration number */
+ if (usbd_set_config_no(sc->sc_udev, UPGT_CONFIG_NO, 0) != 0) {
+ device_printf(dev, "could not set configuration no!\n");
+ return ENXIO;
+ }
+
+ /* get the first interface handle */
+ error = usbd_device2interface_handle(sc->sc_udev, UPGT_IFACE_INDEX,
+ &sc->sc_iface);
+ if (error != 0) {
+ device_printf(dev, "could not get interface handle!\n");
+ return ENXIO;
+ }
+
+ /* find endpoints */
+ id = usbd_get_interface_descriptor(sc->sc_iface);
+ sc->sc_rx_no = sc->sc_tx_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(dev,
+ "no endpoint descriptor for iface %d!\n", i);
+ return ENXIO;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_tx_no = ed->bEndpointAddress;
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_rx_no = ed->bEndpointAddress;
+
+ /*
+ * 0x01 TX pipe
+ * 0x81 RX pipe
+ *
+ * Deprecated scheme (not used with fw version >2.5.6.x):
+ * 0x02 TX MGMT pipe
+ * 0x82 TX MGMT pipe
+ */
+ if (sc->sc_tx_no != -1 && sc->sc_rx_no != -1)
+ break;
+ }
+ if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
+ device_printf(dev, "missing endpoint!\n");
+ return ENXIO;
+ }
+
+ /*
+ * Open TX and RX USB bulk pipes.
+ */
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_tx_pipeh);
+ if (error != 0) {
+ device_printf(dev, "could not open TX pipe: %s!\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_rx_pipeh);
+ if (error != 0) {
+ device_printf(dev, "could not open RX pipe: %s!\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ /* Allocate TX, RX, and CMD xfers. */
+ if (upgt_alloc_tx(sc) != 0)
+ goto fail;
+ if (upgt_alloc_rx(sc) != 0)
+ goto fail;
+ if (upgt_alloc_cmd(sc) != 0)
+ goto fail;
+
+ /* We need the firmware loaded to complete the attach. */
+ return upgt_attach_hook(dev);
+
+fail:
+ device_printf(dev, "%s failed!\n", __func__);
+ return ENXIO;
+}
+
+static int
+upgt_attach_hook(device_t dev)
+{
+ struct ieee80211com *ic;
+ struct ifnet *ifp;
+ struct upgt_softc *sc = device_get_softc(dev);
+ struct upgt_data *data_rx = &sc->rx_data;
+ uint8_t bands;
+ usbd_status error;
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(dev, "can not if_alloc()\n");
+ return ENXIO;
+ }
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ usb_init_task(&sc->sc_mcasttask, upgt_set_multi, sc);
+ usb_init_task(&sc->sc_scantask, upgt_scantask, sc);
+ usb_init_task(&sc->sc_task, upgt_task, sc);
+ usb_init_task(&sc->sc_task_tx, upgt_tx_task, sc);
+ callout_init(&sc->sc_led_ch, 0);
+ callout_init(&sc->sc_watchdog_ch, 0);
+
+ /* Initialize the device. */
+ if (upgt_device_reset(sc) != 0)
+ goto fail;
+
+ /* Verify the firmware. */
+ if (upgt_fw_verify(sc) != 0)
+ goto fail;
+
+ /* Calculate device memory space. */
+ if (sc->sc_memaddr_frame_start == 0 || sc->sc_memaddr_frame_end == 0) {
+ device_printf(dev,
+ "could not find memory space addresses on FW!\n");
+ goto fail;
+ }
+ sc->sc_memaddr_frame_end -= UPGT_MEMSIZE_RX + 1;
+ sc->sc_memaddr_rx_start = sc->sc_memaddr_frame_end + 1;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame start=0x%08x\n",
+ sc->sc_memaddr_frame_start);
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame end=0x%08x\n",
+ sc->sc_memaddr_frame_end);
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address rx start=0x%08x\n",
+ sc->sc_memaddr_rx_start);
+
+ upgt_mem_init(sc);
+
+ /* Load the firmware. */
+ if (upgt_fw_load(sc) != 0)
+ goto fail;
+
+ /* Startup the RX pipe. */
+ usbd_setup_xfer(data_rx->xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf,
+ MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rxeof);
+ error = usbd_transfer(data_rx->xfer);
+ if (error != 0 && error != USBD_IN_PROGRESS) {
+ device_printf(dev, "could not queue RX transfer!\n");
+ goto fail;
+ }
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ /* Read the whole EEPROM content and parse it. */
+ if (upgt_eeprom_read(sc) != 0)
+ goto fail;
+ if (upgt_eeprom_parse(sc) != 0)
+ goto fail;
+
+ /* Setup the 802.11 device. */
+ ifp->if_softc = sc;
+ if_initname(ifp, "upgt", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT; /* USB stack is still under Giant lock */
+ ifp->if_init = upgt_init;
+ ifp->if_ioctl = upgt_ioctl;
+ ifp->if_start = upgt_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic = ifp->if_l2com;
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA;
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_raw_xmit = upgt_raw_xmit;
+ ic->ic_scan_start = upgt_scan_start;
+ ic->ic_scan_end = upgt_scan_end;
+ ic->ic_set_channel = upgt_set_channel;
+
+ ic->ic_vap_create = upgt_vap_create;
+ ic->ic_vap_delete = upgt_vap_delete;
+ ic->ic_update_mcast = upgt_update_mcast;
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap));
+ sc->sc_rxtap_len = sizeof(sc->sc_rxtap);
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(UPGT_RX_RADIOTAP_PRESENT);
+ sc->sc_txtap_len = sizeof(sc->sc_txtap);
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(UPGT_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
+ return 0;
+fail:
+ device_printf(dev, "%s failed!\n", __func__);
+ mtx_destroy(&sc->sc_mtx);
+ if_free(ifp);
+ return ENXIO;
+}
+
+static void
+upgt_tx_task(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ struct upgt_data *data_tx;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_tx_desc *txdesc;
+ struct mbuf *m;
+ uint32_t addr;
+ int len, i;
+ usbd_status error;
+
+ upgt_set_led(sc, UPGT_LED_BLINK);
+
+ UPGT_LOCK(sc);
+ for (i = 0; i < upgt_txbuf; i++) {
+ data_tx = &sc->tx_data[i];
+ if (data_tx->m == NULL)
+ continue;
+
+ m = data_tx->m;
+ addr = data_tx->addr + UPGT_MEMSIZE_FRAME_HEAD;
+
+ /*
+ * Software crypto.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(data_tx->ni, m);
+ if (k == NULL) {
+ device_printf(sc->sc_dev,
+ "ieee80211_crypto_encap returns NULL.\n");
+ goto done;
+ }
+
+ /* in case packet header moved, reset pointer */
+ wh = mtod(m, struct ieee80211_frame *);
+ }
+
+ /*
+ * Transmit the URB containing the TX data.
+ */
+ bzero(data_tx->buf, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_tx->buf;
+ mem->addr = htole32(addr);
+
+ txdesc = (struct upgt_lmac_tx_desc *)(mem + 1);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT) {
+ /* mgmt frames */
+ txdesc->header1.flags = UPGT_H1_FLAGS_TX_MGMT;
+ /* always send mgmt frames at lowest rate (DS1) */
+ memset(txdesc->rates, 0x10, sizeof(txdesc->rates));
+ } else {
+ /* data frames */
+ txdesc->header1.flags = UPGT_H1_FLAGS_TX_DATA;
+ bcopy(sc->sc_cur_rateset, txdesc->rates,
+ sizeof(txdesc->rates));
+ }
+ txdesc->header1.type = UPGT_H1_TYPE_TX_DATA;
+ txdesc->header1.len = htole16(m->m_pkthdr.len);
+ txdesc->header2.reqid = htole32(data_tx->addr);
+ txdesc->header2.type = htole16(UPGT_H2_TYPE_TX_ACK_YES);
+ txdesc->header2.flags = htole16(UPGT_H2_FLAGS_TX_ACK_YES);
+ txdesc->type = htole32(UPGT_TX_DESC_TYPE_DATA);
+ txdesc->pad3[0] = UPGT_TX_DESC_PAD3_SIZE;
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct upgt_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = 0; /* XXX where to get from? */
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m);
+ }
+
+ /* copy frame below our TX descriptor header */
+ m_copydata(m, 0, m->m_pkthdr.len,
+ data_tx->buf + (sizeof(*mem) + sizeof(*txdesc)));
+ /* calculate frame size */
+ len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len;
+ /* we need to align the frame to a 4 byte boundary */
+ len = (len + 3) & ~3;
+ /* calculate frame checksum */
+ mem->chksum = upgt_chksum_le((uint32_t *)txdesc,
+ len - sizeof(*mem));
+ /* we do not need the mbuf anymore */
+ m_freem(m);
+ data_tx->m = NULL;
+
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: TX start data sending\n",
+ __func__);
+ KASSERT(len <= MCLBYTES, ("mbuf is small for saving data"));
+
+ usbd_setup_xfer(data_tx->xfer, sc->sc_tx_pipeh, data_tx,
+ data_tx->buf, len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
+ UPGT_USB_TIMEOUT, upgt_txeof);
+ UPGT_UNLOCK(sc);
+ mtx_lock(&Giant);
+ error = usbd_transfer(data_tx->xfer);
+ mtx_unlock(&Giant);
+ UPGT_LOCK(sc);
+ if (error != 0 && error != USBD_IN_PROGRESS) {
+ device_printf(sc->sc_dev,
+ "could not transmit TX data URB!\n");
+ goto done;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "TX sent (%d bytes)\n", len);
+ }
+done:
+ UPGT_UNLOCK(sc);
+ /*
+ * If we don't regulary read the device statistics, the RX queue
+ * will stall. It's strange, but it works, so we keep reading
+ * the statistics here. *shrug*
+ */
+ (void)upgt_get_stats(sc);
+}
+
+static void
+upgt_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct upgt_data *data_tx = priv;
+ struct upgt_softc *sc = data_tx->sc;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ if (status == USBD_STALLED) {
+ usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
+ return;
+ }
+
+ device_printf(sc->sc_dev, "TX warning(%s)\n",
+ usbd_errstr(status));
+ }
+}
+
+static int
+upgt_get_stats(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_stats *stats;
+ int len;
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ bzero(data_cmd->buf, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ stats = (struct upgt_lmac_stats *)(mem + 1);
+
+ stats->header1.flags = 0;
+ stats->header1.type = UPGT_H1_TYPE_CTRL;
+ stats->header1.len = htole16(
+ sizeof(struct upgt_lmac_stats) - sizeof(struct upgt_lmac_header));
+
+ stats->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ stats->header2.type = htole16(UPGT_H2_TYPE_STATS);
+ stats->header2.flags = 0;
+
+ len = sizeof(*mem) + sizeof(*stats);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)stats,
+ len - sizeof(*mem));
+
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ device_printf(sc->sc_dev,
+ "could not transmit statistics CMD data URB!\n");
+ return (EIO);
+ }
+
+ return (0);
+}
+
+static int
+upgt_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct upgt_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ mtx_lock(&Giant);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ sc->sc_if_flags) &
+ (IFF_ALLMULTI | IFF_PROMISC))
+ upgt_set_multi(sc);
+ } else {
+ upgt_init(sc);
+ startall = 1;
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ upgt_stop(sc, 1);
+ }
+ sc->sc_if_flags = ifp->if_flags;
+ if (startall)
+ ieee80211_start_all(ic);
+ mtx_unlock(&Giant);
+ break;
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCGIFADDR:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static void
+upgt_stop(struct upgt_softc *sc, int disable)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+
+ /* abort and close TX / RX pipes */
+ if (sc->sc_tx_pipeh != NULL)
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ if (sc->sc_rx_pipeh != NULL)
+ usbd_abort_pipe(sc->sc_rx_pipeh);
+
+ /* device down */
+ sc->sc_tx_timer = 0;
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+}
+
+static void
+upgt_task(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct upgt_vap *uvp = UPGT_VAP(vap);
+
+ DPRINTF(sc, UPGT_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[sc->sc_state]);
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_INIT:
+ /* do not accept any frames if the device is down */
+ UPGT_LOCK(sc);
+ upgt_set_macfilter(sc, sc->sc_state);
+ UPGT_UNLOCK(sc);
+ upgt_set_led(sc, UPGT_LED_OFF);
+ break;
+ case IEEE80211_S_SCAN:
+ upgt_set_chan(sc, ic->ic_curchan);
+ break;
+ case IEEE80211_S_AUTH:
+ upgt_set_chan(sc, ic->ic_curchan);
+ break;
+ case IEEE80211_S_ASSOC:
+ break;
+ case IEEE80211_S_RUN:
+ UPGT_LOCK(sc);
+ upgt_set_macfilter(sc, sc->sc_state);
+ UPGT_UNLOCK(sc);
+ upgt_set_led(sc, UPGT_LED_ON);
+ break;
+ default:
+ break;
+ }
+
+ IEEE80211_LOCK(ic);
+ uvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+upgt_set_led(struct upgt_softc *sc, int action)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_led *led;
+ int len;
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ bzero(data_cmd->buf, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ led = (struct upgt_lmac_led *)(mem + 1);
+
+ led->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ led->header1.type = UPGT_H1_TYPE_CTRL;
+ led->header1.len = htole16(
+ sizeof(struct upgt_lmac_led) -
+ sizeof(struct upgt_lmac_header));
+
+ led->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ led->header2.type = htole16(UPGT_H2_TYPE_LED);
+ led->header2.flags = 0;
+
+ switch (action) {
+ case UPGT_LED_OFF:
+ led->mode = htole16(UPGT_LED_MODE_SET);
+ led->action_fix = 0;
+ led->action_tmp = htole16(UPGT_LED_ACTION_OFF);
+ led->action_tmp_dur = 0;
+ break;
+ case UPGT_LED_ON:
+ led->mode = htole16(UPGT_LED_MODE_SET);
+ led->action_fix = 0;
+ led->action_tmp = htole16(UPGT_LED_ACTION_ON);
+ led->action_tmp_dur = 0;
+ break;
+ case UPGT_LED_BLINK:
+ if (sc->sc_state != IEEE80211_S_RUN)
+ return;
+ if (sc->sc_led_blink)
+ /* previous blink was not finished */
+ return;
+ led->mode = htole16(UPGT_LED_MODE_SET);
+ led->action_fix = htole16(UPGT_LED_ACTION_OFF);
+ led->action_tmp = htole16(UPGT_LED_ACTION_ON);
+ led->action_tmp_dur = htole16(UPGT_LED_ACTION_TMP_DUR);
+ /* lock blink */
+ sc->sc_led_blink = 1;
+ callout_reset(&sc->sc_led_ch, hz, upgt_set_led_blink, sc);
+ break;
+ default:
+ return;
+ }
+
+ len = sizeof(*mem) + sizeof(*led);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)led,
+ len - sizeof(*mem));
+
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0)
+ device_printf(sc->sc_dev, "could not transmit led CMD URB!\n");
+}
+
+static void
+upgt_set_led_blink(void *arg)
+{
+ struct upgt_softc *sc = arg;
+
+ /* blink finished, we are ready for a next one */
+ sc->sc_led_blink = 0;
+}
+
+static void
+upgt_init(void *priv)
+{
+ struct upgt_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ UPGT_LOCK(sc);
+ upgt_init_locked(sc);
+ UPGT_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+upgt_init_locked(struct upgt_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ DPRINTF(sc, UPGT_DEBUG_RESET, "setting MAC address to %s\n",
+ ether_sprintf(ic->ic_myaddr));
+
+ upgt_set_macfilter(sc, IEEE80211_S_SCAN);
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+}
+
+static int
+upgt_set_macfilter(struct upgt_softc *sc, uint8_t state)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_filter *filter;
+ int len;
+ uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ bzero(data_cmd->buf, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ filter = (struct upgt_lmac_filter *)(mem + 1);
+
+ filter->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ filter->header1.type = UPGT_H1_TYPE_CTRL;
+ filter->header1.len = htole16(
+ sizeof(struct upgt_lmac_filter) -
+ sizeof(struct upgt_lmac_header));
+
+ filter->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ filter->header2.type = htole16(UPGT_H2_TYPE_MACFILTER);
+ filter->header2.flags = 0;
+
+ switch (state) {
+ case IEEE80211_S_INIT:
+ DPRINTF(sc, UPGT_DEBUG_STATE, "%s: set MAC filter to INIT\n",
+ __func__);
+ filter->type = htole16(UPGT_FILTER_TYPE_RESET);
+ break;
+ case IEEE80211_S_SCAN:
+ DPRINTF(sc, UPGT_DEBUG_STATE,
+ "set MAC filter to SCAN (bssid %s)\n",
+ ether_sprintf(broadcast));
+ filter->type = htole16(UPGT_FILTER_TYPE_NONE);
+ IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(filter->src, broadcast);
+ filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2);
+ filter->rxhw = htole32(sc->sc_eeprom_hwrx);
+ filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3);
+ break;
+ case IEEE80211_S_RUN:
+ /* XXX monitor mode isn't tested yet. */
+ if (vap->iv_opmode == IEEE80211_M_MONITOR) {
+ filter->type = htole16(UPGT_FILTER_TYPE_MONITOR);
+ IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid);
+ filter->unknown1 = htole16(UPGT_FILTER_MONITOR_UNKNOWN1);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->unknown2 = htole16(UPGT_FILTER_MONITOR_UNKNOWN2);
+ filter->rxhw = htole32(sc->sc_eeprom_hwrx);
+ filter->unknown3 = htole16(UPGT_FILTER_MONITOR_UNKNOWN3);
+ } else {
+ DPRINTF(sc, UPGT_DEBUG_STATE,
+ "set MAC filter to RUN (bssid %s)\n",
+ ether_sprintf(ni->ni_bssid));
+ filter->type = htole16(UPGT_FILTER_TYPE_STA);
+ IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid);
+ filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2);
+ filter->rxhw = htole32(sc->sc_eeprom_hwrx);
+ filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3);
+ }
+ break;
+ default:
+ device_printf(sc->sc_dev,
+ "MAC filter does not know that state!\n");
+ break;
+ }
+
+ len = sizeof(*mem) + sizeof(*filter);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)filter,
+ len - sizeof(*mem));
+
+ UPGT_UNLOCK(sc);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ device_printf(sc->sc_dev,
+ "could not transmit macfilter CMD data URB!\n");
+ UPGT_LOCK(sc);
+ return (EIO);
+ }
+ UPGT_LOCK(sc);
+
+ return (0);
+}
+
+static void
+upgt_setup_rates(struct ieee80211vap *vap, struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct upgt_softc *sc = ifp->if_softc;
+ const struct ieee80211_txparam *tp;
+
+ /*
+ * 0x01 = OFMD6 0x10 = DS1
+ * 0x04 = OFDM9 0x11 = DS2
+ * 0x06 = OFDM12 0x12 = DS5
+ * 0x07 = OFDM18 0x13 = DS11
+ * 0x08 = OFDM24
+ * 0x09 = OFDM36
+ * 0x0a = OFDM48
+ * 0x0b = OFDM54
+ */
+ const uint8_t rateset_auto_11b[] =
+ { 0x13, 0x13, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10 };
+ const uint8_t rateset_auto_11g[] =
+ { 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x04, 0x01 };
+ const uint8_t rateset_fix_11bg[] =
+ { 0x10, 0x11, 0x12, 0x13, 0x01, 0x04, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b };
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ /* XXX */
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
+ /*
+ * Automatic rate control is done by the device.
+ * We just pass the rateset from which the device
+ * will pickup a rate.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ bcopy(rateset_auto_11b, sc->sc_cur_rateset,
+ sizeof(sc->sc_cur_rateset));
+ if (ic->ic_curmode == IEEE80211_MODE_11G ||
+ ic->ic_curmode == IEEE80211_MODE_AUTO)
+ bcopy(rateset_auto_11g, sc->sc_cur_rateset,
+ sizeof(sc->sc_cur_rateset));
+ } else {
+ /* set a fixed rate */
+ memset(sc->sc_cur_rateset, rateset_fix_11bg[tp->ucastrate],
+ sizeof(sc->sc_cur_rateset));
+ }
+}
+
+static void
+upgt_set_multi(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if (!(ifp->if_flags & IFF_UP))
+ return;
+
+ /*
+ * XXX don't know how to set a device. Lack of docs. Just try to set
+ * IFF_ALLMULTI flag here.
+ */
+ IF_ADDR_LOCK(ifp);
+ ifp->if_flags |= IFF_ALLMULTI;
+ IF_ADDR_UNLOCK(ifp);
+}
+
+static void
+upgt_start(struct ifnet *ifp)
+{
+ struct upgt_softc *sc = ifp->if_softc;
+ struct upgt_data *data_tx;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ int i;
+
+ UPGT_LOCK(sc);
+ for (i = 0; i < upgt_txbuf; i++) {
+ data_tx = &sc->tx_data[i];
+ if (data_tx->use == 1)
+ continue;
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+
+ if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) {
+ device_printf(sc->sc_dev, "no free prism memory!\n");
+ UPGT_UNLOCK(sc);
+ return;
+ }
+ data_tx->ni = ni;
+ data_tx->m = m;
+ data_tx->use = 1;
+ sc->tx_queued++;
+ }
+
+ if (sc->tx_queued > 0) {
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "tx_queued=%d\n", sc->tx_queued);
+
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc);
+ /* process the TX queue in process context */
+ usb_rem_task(sc->sc_udev, &sc->sc_task_tx);
+ usb_add_task(sc->sc_udev, &sc->sc_task_tx, USB_TASKQ_DRIVER);
+ }
+ UPGT_UNLOCK(sc);
+}
+
+static int
+upgt_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct upgt_softc *sc = ifp->if_softc;
+ struct upgt_data *data_tx = NULL;
+ int i;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+
+ UPGT_LOCK(sc);
+ if (sc->tx_queued >= upgt_txbuf) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ UPGT_UNLOCK(sc);
+ return ENOBUFS; /* XXX */
+ }
+
+ ifp->if_opackets++;
+
+ /* choose a unused buffer. */
+ for (i = 0; i < upgt_txbuf; i++) {
+ data_tx = &sc->tx_data[i];
+ if (data_tx->use == 0)
+ break;
+ }
+ KASSERT(data_tx != NULL, ("data_tx is NULL"));
+ KASSERT(data_tx->use == 0, ("no empty TX queue"));
+ if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) {
+ device_printf(sc->sc_dev, "no free prism memory!\n");
+ UPGT_UNLOCK(sc);
+ return ENOBUFS;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct upgt_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = 0; /* TODO: where to get from? */
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m);
+ }
+
+ data_tx->ni = ni;
+ data_tx->m = m;
+ data_tx->use = 1;
+ sc->tx_queued++;
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ UPGT_UNLOCK(sc);
+
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc);
+ usb_rem_task(sc->sc_udev, &sc->sc_task_tx);
+ usb_add_task(sc->sc_udev, &sc->sc_task_tx, USB_TASKQ_DRIVER);
+
+ return 0;
+}
+
+static void
+upgt_watchdog(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if (sc->sc_tx_timer > 0) {
+ if (--sc->sc_tx_timer == 0) {
+ device_printf(sc->sc_dev, "watchdog timeout\n");
+ /* upgt_init(ifp); XXX needs a process context ? */
+ ifp->if_oerrors++;
+ return;
+ }
+ callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc);
+ }
+}
+
+static uint32_t
+upgt_mem_alloc(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_memory.pages; i++) {
+ if (sc->sc_memory.page[i].used == 0) {
+ sc->sc_memory.page[i].used = 1;
+ return (sc->sc_memory.page[i].addr);
+ }
+ }
+
+ return (0);
+}
+
+static void
+upgt_scantask(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ switch (sc->sc_scan_action) {
+ case UPGT_SET_CHANNEL:
+ upgt_set_chan(sc, ic->ic_curchan);
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown scan action %d\n",
+ sc->sc_scan_action);
+ break;
+ }
+}
+
+static void
+upgt_scan_start(struct ieee80211com *ic)
+{
+ /* do nothing. */
+}
+
+static void
+upgt_scan_end(struct ieee80211com *ic)
+{
+ /* do nothing. */
+}
+
+static void
+upgt_set_channel(struct ieee80211com *ic)
+{
+ struct upgt_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = UPGT_SET_CHANNEL;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+upgt_set_chan(struct upgt_softc *sc, struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_channel *chan;
+ int len, channel;
+
+ channel = ieee80211_chan2ieee(ic, c);
+ if (channel == 0 || channel == IEEE80211_CHAN_ANY) {
+ /* XXX should NEVER happen */
+ device_printf(sc->sc_dev,
+ "%s: invalid channel %x\n", __func__, channel);
+ return;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_STATE, "%s: channel %d\n", __func__, channel);
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ bzero(data_cmd->buf, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ chan = (struct upgt_lmac_channel *)(mem + 1);
+
+ chan->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ chan->header1.type = UPGT_H1_TYPE_CTRL;
+ chan->header1.len = htole16(
+ sizeof(struct upgt_lmac_channel) - sizeof(struct upgt_lmac_header));
+
+ chan->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ chan->header2.type = htole16(UPGT_H2_TYPE_CHANNEL);
+ chan->header2.flags = 0;
+
+ chan->unknown1 = htole16(UPGT_CHANNEL_UNKNOWN1);
+ chan->unknown2 = htole16(UPGT_CHANNEL_UNKNOWN2);
+ chan->freq6 = sc->sc_eeprom_freq6[channel];
+ chan->settings = sc->sc_eeprom_freq6_settings;
+ chan->unknown3 = UPGT_CHANNEL_UNKNOWN3;
+
+ bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_1,
+ sizeof(chan->freq3_1));
+ bcopy(&sc->sc_eeprom_freq4[channel], chan->freq4,
+ sizeof(sc->sc_eeprom_freq4[channel]));
+ bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_2,
+ sizeof(chan->freq3_2));
+
+ len = sizeof(*mem) + sizeof(*chan);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)chan,
+ len - sizeof(*mem));
+
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0)
+ device_printf(sc->sc_dev,
+ "could not transmit channel CMD data URB!\n");
+}
+
+static struct ieee80211vap *
+upgt_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct upgt_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ uvp = (struct upgt_vap *) malloc(sizeof(struct upgt_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (uvp == NULL)
+ return NULL;
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = upgt_newstate;
+
+ /* setup device rates */
+ upgt_setup_rates(vap, ic);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static int
+upgt_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct upgt_vap *uvp = UPGT_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct upgt_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+
+ if (nstate == IEEE80211_S_INIT) {
+ uvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
+ usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
+ return EINPROGRESS;
+ }
+}
+
+static void
+upgt_vap_delete(struct ieee80211vap *vap)
+{
+ struct upgt_vap *uvp = UPGT_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static void
+upgt_update_mcast(struct ifnet *ifp)
+{
+ struct upgt_softc *sc = ifp->if_softc;
+
+ usb_add_task(sc->sc_udev, &sc->sc_mcasttask, USB_TASKQ_DRIVER);
+}
+
+static int
+upgt_eeprom_parse(struct upgt_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct upgt_eeprom_header *eeprom_header;
+ struct upgt_eeprom_option *eeprom_option;
+ uint16_t option_len;
+ uint16_t option_type;
+ uint16_t preamble_len;
+ int option_end = 0;
+
+ /* calculate eeprom options start offset */
+ eeprom_header = (struct upgt_eeprom_header *)sc->sc_eeprom;
+ preamble_len = le16toh(eeprom_header->preamble_len);
+ eeprom_option = (struct upgt_eeprom_option *)(sc->sc_eeprom +
+ (sizeof(struct upgt_eeprom_header) + preamble_len));
+
+ while (!option_end) {
+ /* the eeprom option length is stored in words */
+ option_len =
+ (le16toh(eeprom_option->len) - 1) * sizeof(uint16_t);
+ option_type =
+ le16toh(eeprom_option->type);
+
+ switch (option_type) {
+ case UPGT_EEPROM_TYPE_NAME:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM name len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_SERIAL:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM serial len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_MAC:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM mac len=%d\n", option_len);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, eeprom_option->data);
+ break;
+ case UPGT_EEPROM_TYPE_HWRX:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM hwrx len=%d\n", option_len);
+
+ upgt_eeprom_parse_hwrx(sc, eeprom_option->data);
+ break;
+ case UPGT_EEPROM_TYPE_CHIP:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM chip len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ3:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq3 len=%d\n", option_len);
+
+ upgt_eeprom_parse_freq3(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ4:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq4 len=%d\n", option_len);
+
+ upgt_eeprom_parse_freq4(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ5:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq5 len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ6:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq6 len=%d\n", option_len);
+
+ upgt_eeprom_parse_freq6(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_END:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM end len=%d\n", option_len);
+ option_end = 1;
+ break;
+ case UPGT_EEPROM_TYPE_OFF:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "%s: EEPROM off without end option!\n", __func__);
+ return (EIO);
+ default:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM unknown type 0x%04x len=%d\n",
+ option_type, option_len);
+ break;
+ }
+
+ /* jump to next EEPROM option */
+ eeprom_option = (struct upgt_eeprom_option *)
+ (eeprom_option->data + option_len);
+ }
+
+ return (0);
+}
+
+static void
+upgt_eeprom_parse_freq3(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_eeprom_freq3_header *freq3_header;
+ struct upgt_lmac_freq3 *freq3;
+ int i, elements, flags;
+ unsigned channel;
+
+ freq3_header = (struct upgt_eeprom_freq3_header *)data;
+ freq3 = (struct upgt_lmac_freq3 *)(freq3_header + 1);
+
+ flags = freq3_header->flags;
+ elements = freq3_header->elements;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d\n",
+ flags, elements);
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(le16toh(freq3[i].freq), 0);
+ if (!(channel >= 0 && channel < IEEE80211_CHAN_MAX))
+ continue;
+
+ sc->sc_eeprom_freq3[channel] = freq3[i];
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n",
+ le16toh(sc->sc_eeprom_freq3[channel].freq), channel);
+ }
+}
+
+void
+upgt_eeprom_parse_freq4(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_eeprom_freq4_header *freq4_header;
+ struct upgt_eeprom_freq4_1 *freq4_1;
+ struct upgt_eeprom_freq4_2 *freq4_2;
+ int i, j, elements, settings, flags;
+ unsigned channel;
+
+ freq4_header = (struct upgt_eeprom_freq4_header *)data;
+ freq4_1 = (struct upgt_eeprom_freq4_1 *)(freq4_header + 1);
+ flags = freq4_header->flags;
+ elements = freq4_header->elements;
+ settings = freq4_header->settings;
+
+ /* we need this value later */
+ sc->sc_eeprom_freq6_settings = freq4_header->settings;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d settings=%d\n",
+ flags, elements, settings);
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(le16toh(freq4_1[i].freq), 0);
+ if (!(channel >= 0 && channel < IEEE80211_CHAN_MAX))
+ continue;
+
+ freq4_2 = (struct upgt_eeprom_freq4_2 *)freq4_1[i].data;
+ for (j = 0; j < settings; j++) {
+ sc->sc_eeprom_freq4[channel][j].cmd = freq4_2[j];
+ sc->sc_eeprom_freq4[channel][j].pad = 0;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n",
+ le16toh(freq4_1[i].freq), channel);
+ }
+}
+
+void
+upgt_eeprom_parse_freq6(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_lmac_freq6 *freq6;
+ int i, elements;
+ unsigned channel;
+
+ freq6 = (struct upgt_lmac_freq6 *)data;
+ elements = len / sizeof(struct upgt_lmac_freq6);
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "elements=%d\n", elements);
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(le16toh(freq6[i].freq), 0);
+ if (!(channel >= 0 && channel < IEEE80211_CHAN_MAX))
+ continue;
+
+ sc->sc_eeprom_freq6[channel] = freq6[i];
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n",
+ le16toh(sc->sc_eeprom_freq6[channel].freq), channel);
+ }
+}
+
+static void
+upgt_eeprom_parse_hwrx(struct upgt_softc *sc, uint8_t *data)
+{
+ struct upgt_eeprom_option_hwrx *option_hwrx;
+
+ option_hwrx = (struct upgt_eeprom_option_hwrx *)data;
+
+ sc->sc_eeprom_hwrx = option_hwrx->rxfilter - UPGT_EEPROM_RX_CONST;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "hwrx option value=0x%04x\n",
+ sc->sc_eeprom_hwrx);
+}
+
+static int
+upgt_eeprom_read(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_eeprom *eeprom;
+ int offset, block, len;
+
+ offset = 0;
+ block = UPGT_EEPROM_BLOCK_SIZE;
+ while (offset < UPGT_EEPROM_SIZE) {
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "request EEPROM block (offset=%d, len=%d)\n", offset, block);
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ bzero(data_cmd->buf, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ eeprom = (struct upgt_lmac_eeprom *)(mem + 1);
+ eeprom->header1.flags = 0;
+ eeprom->header1.type = UPGT_H1_TYPE_CTRL;
+ eeprom->header1.len = htole16((
+ sizeof(struct upgt_lmac_eeprom) -
+ sizeof(struct upgt_lmac_header)) + block);
+
+ eeprom->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ eeprom->header2.type = htole16(UPGT_H2_TYPE_EEPROM);
+ eeprom->header2.flags = 0;
+
+ eeprom->offset = htole16(offset);
+ eeprom->len = htole16(block);
+
+ len = sizeof(*mem) + sizeof(*eeprom) + block;
+
+ mem->chksum = upgt_chksum_le((uint32_t *)eeprom,
+ len - sizeof(*mem));
+
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len,
+ USBD_FORCE_SHORT_XFER) != 0) {
+ device_printf(sc->sc_dev,
+ "could not transmit EEPROM data URB!\n");
+ return (EIO);
+ }
+ if (tsleep(sc, 0, "eeprom_request", UPGT_USB_TIMEOUT)) {
+ device_printf(sc->sc_dev,
+ "timeout while waiting for EEPROM data!\n");
+ return (EIO);
+ }
+
+ offset += block;
+ if (UPGT_EEPROM_SIZE - offset < block)
+ block = UPGT_EEPROM_SIZE - offset;
+ }
+
+ return (0);
+}
+
+/*
+ * The firmware awaits a checksum for each frame we send to it.
+ * The algorithm used therefor is uncommon but somehow similar to CRC32.
+ */
+static uint32_t
+upgt_chksum_le(const uint32_t *buf, size_t size)
+{
+ int i;
+ uint32_t crc = 0;
+
+ for (i = 0; i < size; i += sizeof(uint32_t)) {
+ crc = htole32(crc ^ *buf++);
+ crc = htole32((crc >> 5) ^ (crc << 3));
+ }
+
+ return (crc);
+}
+
+static void
+upgt_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct upgt_data *data_rx = priv;
+ struct upgt_softc *sc = data_rx->sc;
+ int len;
+ struct upgt_lmac_header *header;
+ struct upgt_lmac_eeprom *eeprom;
+ uint8_t h1_type;
+ uint16_t h2_type;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
+ goto skip;
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ /*
+ * Check what type of frame came in.
+ */
+ header = (struct upgt_lmac_header *)(data_rx->buf + 4);
+
+ h1_type = header->header1.type;
+ h2_type = le16toh(header->header2.type);
+
+ if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_EEPROM) {
+ eeprom = (struct upgt_lmac_eeprom *)(data_rx->buf + 4);
+ uint16_t eeprom_offset = le16toh(eeprom->offset);
+ uint16_t eeprom_len = le16toh(eeprom->len);
+
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "received EEPROM block (offset=%d, len=%d)\n",
+ eeprom_offset, eeprom_len);
+
+ bcopy(data_rx->buf + sizeof(struct upgt_lmac_eeprom) + 4,
+ sc->sc_eeprom + eeprom_offset, eeprom_len);
+
+ /* EEPROM data has arrived in time, wakeup tsleep() */
+ wakeup(sc);
+ } else if (h1_type == UPGT_H1_TYPE_CTRL &&
+ h2_type == UPGT_H2_TYPE_TX_DONE) {
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: received 802.11 TX done\n",
+ __func__);
+ upgt_tx_done(sc, data_rx->buf + 4);
+ } else if (h1_type == UPGT_H1_TYPE_RX_DATA ||
+ h1_type == UPGT_H1_TYPE_RX_DATA_MGMT) {
+ DPRINTF(sc, UPGT_DEBUG_RECV, "%s: received 802.11 RX data\n",
+ __func__);
+ upgt_rx(sc, data_rx->buf + 4, le16toh(header->header1.len));
+ } else if (h1_type == UPGT_H1_TYPE_CTRL &&
+ h2_type == UPGT_H2_TYPE_STATS) {
+ DPRINTF(sc, UPGT_DEBUG_STAT, "%s: received statistic data\n",
+ __func__);
+ /* TODO: what could we do with the statistic data? */
+ } else {
+ /* ignore unknown frame types */
+ DPRINTF(sc, UPGT_DEBUG_INTR,
+ "received unknown frame type 0x%02x\n",
+ header->header1.type);
+ }
+
+skip: /* setup new transfer */
+ usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf, MCLBYTES,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rxeof);
+ (void)usbd_transfer(xfer);
+}
+
+static void
+upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct upgt_lmac_rx_desc *rxdesc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ int nf;
+
+ /*
+ * don't pass packets to the ieee80211 framework if the driver isn't
+ * RUNNING.
+ */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ return;
+
+ /* access RX packet descriptor */
+ rxdesc = (struct upgt_lmac_rx_desc *)data;
+
+ /* create mbuf which is suitable for strict alignment archs */
+ KASSERT((pkglen + ETHER_ALIGN) < MCLBYTES,
+ ("A current mbuf storage is small (%d)", pkglen + ETHER_ALIGN));
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ device_printf(sc->sc_dev, "could not create RX mbuf!\n");
+ return;
+ }
+ m_adj(m, ETHER_ALIGN);
+ bcopy(rxdesc->data, mtod(m, char *), pkglen);
+ /* trim FCS */
+ m->m_len = m->m_pkthdr.len = pkglen - IEEE80211_CRC_LEN;
+ m->m_pkthdr.rcvif = ifp;
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct upgt_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ tap->wr_rate = upgt_rx_rate(sc, rxdesc->rate);
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_antsignal = rxdesc->rssi;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+ ifp->if_ipackets++;
+
+ nf = -95; /* XXX */
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void)ieee80211_input(ni, m, rxdesc->rssi, nf, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void)ieee80211_input_all(ic, m, rxdesc->rssi, nf, 0);
+
+ DPRINTF(sc, UPGT_DEBUG_RX_PROC, "%s: RX done\n", __func__);
+}
+
+static uint8_t
+upgt_rx_rate(struct upgt_softc *sc, const int rate)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ static const uint8_t cck_upgt2rate[4] = { 2, 4, 11, 22 };
+ static const uint8_t ofdm_upgt2rate[12] =
+ { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 };
+
+ if (ic->ic_curmode == IEEE80211_MODE_11B &&
+ !(rate < 0 || rate > 3))
+ return cck_upgt2rate[rate & 0xf];
+
+ if (ic->ic_curmode == IEEE80211_MODE_11G &&
+ !(rate < 0 || rate > 11))
+ return ofdm_upgt2rate[rate & 0xf];
+
+ return (0);
+}
+
+static void
+upgt_tx_done(struct upgt_softc *sc, uint8_t *data)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct upgt_lmac_tx_done_desc *desc;
+ int i;
+
+ desc = (struct upgt_lmac_tx_done_desc *)data;
+
+ UPGT_LOCK(sc);
+ for (i = 0; i < upgt_txbuf; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ if (data_tx->addr == le32toh(desc->header2.reqid)) {
+ upgt_mem_free(sc, data_tx->addr);
+ ieee80211_free_node(data_tx->ni);
+ data_tx->ni = NULL;
+ data_tx->addr = 0;
+ data_tx->m = NULL;
+ data_tx->use = 0;
+
+ sc->tx_queued--;
+ ifp->if_opackets++;
+
+ DPRINTF(sc, UPGT_DEBUG_TX_PROC,
+ "TX done: memaddr=0x%08x, status=0x%04x, rssi=%d, ",
+ le32toh(desc->header2.reqid),
+ le16toh(desc->status), le16toh(desc->rssi));
+ DPRINTF(sc, UPGT_DEBUG_TX_PROC, "seq=%d\n",
+ le16toh(desc->seq));
+ break;
+ }
+ }
+ if (sc->tx_queued == 0) {
+ /* TX queued was processed, continue */
+ sc->sc_tx_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ UPGT_UNLOCK(sc);
+ upgt_start(ifp);
+ return;
+ }
+ UPGT_UNLOCK(sc);
+}
+
+static void
+upgt_mem_free(struct upgt_softc *sc, uint32_t addr)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_memory.pages; i++) {
+ if (sc->sc_memory.page[i].addr == addr) {
+ sc->sc_memory.page[i].used = 0;
+ return;
+ }
+ }
+
+ device_printf(sc->sc_dev,
+ "could not free memory address 0x%08x!\n", addr);
+}
+
+static int
+upgt_fw_load(struct upgt_softc *sc)
+{
+ const struct firmware *fw;
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_data *data_rx = &sc->rx_data;
+ struct upgt_fw_x2_header *x2;
+ char start_fwload_cmd[] = { 0x3c, 0x0d };
+ int error = 0, offset, bsize, n, i, len;
+ uint32_t crc32;
+
+ fw = firmware_get(upgt_fwname);
+ if (fw == NULL) {
+ device_printf(sc->sc_dev, "could not read microcode %s!\n",
+ upgt_fwname);
+ return EIO;
+ }
+
+ /* send firmware start load command */
+ len = sizeof(start_fwload_cmd);
+ bcopy(start_fwload_cmd, data_cmd->buf, len);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ device_printf(sc->sc_dev,
+ "could not send start_firmware_load command!\n");
+ error = EIO;
+ goto fail;
+ }
+
+ /* send X2 header */
+ len = sizeof(struct upgt_fw_x2_header);
+ x2 = (struct upgt_fw_x2_header *)data_cmd->buf;
+ bcopy(UPGT_X2_SIGNATURE, x2->signature, UPGT_X2_SIGNATURE_SIZE);
+ x2->startaddr = htole32(UPGT_MEMADDR_FIRMWARE_START);
+ x2->len = htole32(fw->datasize);
+ x2->crc = upgt_crc32_le((uint8_t *)data_cmd->buf +
+ UPGT_X2_SIGNATURE_SIZE,
+ sizeof(struct upgt_fw_x2_header) - UPGT_X2_SIGNATURE_SIZE -
+ sizeof(uint32_t));
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ device_printf(sc->sc_dev,
+ "could not send firmware X2 header!\n");
+ error = EIO;
+ goto fail;
+ }
+
+ /* download firmware */
+ for (offset = 0; offset < fw->datasize; offset += bsize) {
+ if (fw->datasize - offset > UPGT_FW_BLOCK_SIZE)
+ bsize = UPGT_FW_BLOCK_SIZE;
+ else
+ bsize = fw->datasize - offset;
+
+ n = upgt_fw_copy((const uint8_t *)fw->data + offset,
+ data_cmd->buf, bsize);
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "FW offset=%d, read=%d, sent=%d\n",
+ offset, n, bsize);
+
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &bsize, 0)
+ != 0) {
+ device_printf(sc->sc_dev,
+ "error while downloading firmware block!\n");
+ error = EIO;
+ goto fail;
+ }
+
+ bsize = n;
+ }
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware downloaded\n", __func__);
+
+ /* load firmware */
+ crc32 = upgt_crc32_le(fw->data, fw->datasize);
+ *((uint32_t *)(data_cmd->buf) ) = crc32;
+ *((uint8_t *)(data_cmd->buf) + 4) = 'g';
+ *((uint8_t *)(data_cmd->buf) + 5) = '\r';
+ len = 6;
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ device_printf(sc->sc_dev,
+ "could not send load_firmware command!\n");
+ error = EIO;
+ goto fail;
+ }
+
+ for (i = 0; i < UPGT_FIRMWARE_TIMEOUT; i++) {
+ len = UPGT_FW_BLOCK_SIZE;
+ bzero(data_rx->buf, MCLBYTES);
+ if (upgt_bulk_xmit(sc, data_rx, sc->sc_rx_pipeh, &len,
+ USBD_SHORT_XFER_OK) != 0) {
+ device_printf(sc->sc_dev,
+ "could not read firmware response!\n");
+ error = EIO;
+ goto fail;
+ }
+
+ if (memcmp(data_rx->buf, "OK", 2) == 0)
+ break; /* firmware load was successful */
+ }
+ if (i == UPGT_FIRMWARE_TIMEOUT) {
+ device_printf(sc->sc_dev, "firmware load failed!\n");
+ error = EIO;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware loaded\n", __func__);
+fail:
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ return (error);
+}
+
+static uint32_t
+upgt_crc32_le(const void *buf, size_t size)
+{
+ uint32_t crc;
+
+ crc = ether_crc32_le(buf, size);
+
+ /* apply final XOR value as common for CRC-32 */
+ crc = htole32(crc ^ 0xffffffffU);
+
+ return (crc);
+}
+
+/*
+ * While copying the version 2 firmware, we need to replace two characters:
+ *
+ * 0x7e -> 0x7d 0x5e
+ * 0x7d -> 0x7d 0x5d
+ */
+static int
+upgt_fw_copy(const uint8_t *src, char *dst, int size)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < size && j < size; i++) {
+ switch (src[i]) {
+ case 0x7e:
+ dst[j] = 0x7d;
+ j++;
+ dst[j] = 0x5e;
+ j++;
+ break;
+ case 0x7d:
+ dst[j] = 0x7d;
+ j++;
+ dst[j] = 0x5d;
+ j++;
+ break;
+ default:
+ dst[j] = src[i];
+ j++;
+ break;
+ }
+ }
+
+ return (i);
+}
+
+static int
+upgt_mem_init(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) {
+ sc->sc_memory.page[i].used = 0;
+
+ if (i == 0) {
+ /*
+ * The first memory page is always reserved for
+ * command data.
+ */
+ sc->sc_memory.page[i].addr =
+ sc->sc_memaddr_frame_start + MCLBYTES;
+ } else {
+ sc->sc_memory.page[i].addr =
+ sc->sc_memory.page[i - 1].addr + MCLBYTES;
+ }
+
+ if (sc->sc_memory.page[i].addr + MCLBYTES >=
+ sc->sc_memaddr_frame_end)
+ break;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address page %d=0x%08x\n",
+ i, sc->sc_memory.page[i].addr);
+ }
+
+ sc->sc_memory.pages = i;
+ if (upgt_txbuf > sc->sc_memory.pages)
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory pages=%d\n", sc->sc_memory.pages);
+ return (0);
+}
+
+static int
+upgt_fw_verify(struct upgt_softc *sc)
+{
+ const struct firmware *fw;
+ const struct upgt_fw_bra_option *bra_opt;
+ const struct upgt_fw_bra_descr *descr;
+ const uint8_t *p;
+ const uint32_t *uc;
+ uint32_t bra_option_type, bra_option_len;
+ int offset, bra_end = 0, error = 0;
+
+ fw = firmware_get(upgt_fwname);
+ if (fw == NULL) {
+ device_printf(sc->sc_dev, "could not read microcode %s!\n",
+ upgt_fwname);
+ return EIO;
+ }
+
+ /*
+ * Seek to beginning of Boot Record Area (BRA).
+ */
+ for (offset = 0; offset < fw->datasize; offset += sizeof(*uc)) {
+ uc = (const uint32_t *)((const uint8_t *)fw->data + offset);
+ if (*uc == 0)
+ break;
+ }
+ for (; offset < fw->datasize; offset += sizeof(*uc)) {
+ uc = (const uint32_t *)((const uint8_t *)fw->data + offset);
+ if (*uc != 0)
+ break;
+ }
+ if (offset == fw->datasize) {
+ device_printf(sc->sc_dev,
+ "firmware Boot Record Area not found!\n");
+ error = EIO;
+ goto fail;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "firmware Boot Record Area found at offset %d\n", offset);
+
+ /*
+ * Parse Boot Record Area (BRA) options.
+ */
+ while (offset < fw->datasize && bra_end == 0) {
+ /* get current BRA option */
+ p = (const uint8_t *)fw->data + offset;
+ bra_opt = (const struct upgt_fw_bra_option *)p;
+ bra_option_type = le32toh(bra_opt->type);
+ bra_option_len = le32toh(bra_opt->len) * sizeof(*uc);
+
+ switch (bra_option_type) {
+ case UPGT_BRA_TYPE_FW:
+ DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_FW len=%d\n",
+ bra_option_len);
+
+ if (bra_option_len != UPGT_BRA_FWTYPE_SIZE) {
+ device_printf(sc->sc_dev,
+ "wrong UPGT_BRA_TYPE_FW len!\n");
+ error = EIO;
+ goto fail;
+ }
+ if (memcmp(UPGT_BRA_FWTYPE_LM86, bra_opt->data,
+ bra_option_len) == 0) {
+ sc->sc_fw_type = UPGT_FWTYPE_LM86;
+ break;
+ }
+ if (memcmp(UPGT_BRA_FWTYPE_LM87, bra_opt->data,
+ bra_option_len) == 0) {
+ sc->sc_fw_type = UPGT_FWTYPE_LM87;
+ break;
+ }
+ device_printf(sc->sc_dev,
+ "unsupported firmware type!\n");
+ error = EIO;
+ goto fail;
+ case UPGT_BRA_TYPE_VERSION:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_VERSION len=%d\n", bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_DEPIF:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_DEPIF len=%d\n", bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_EXPIF:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_EXPIF len=%d\n", bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_DESCR:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_DESCR len=%d\n", bra_option_len);
+
+ descr = (const struct upgt_fw_bra_descr *)bra_opt->data;
+
+ sc->sc_memaddr_frame_start =
+ le32toh(descr->memaddr_space_start);
+ sc->sc_memaddr_frame_end =
+ le32toh(descr->memaddr_space_end);
+
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "memory address space start=0x%08x\n",
+ sc->sc_memaddr_frame_start);
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "memory address space end=0x%08x\n",
+ sc->sc_memaddr_frame_end);
+ break;
+ case UPGT_BRA_TYPE_END:
+ DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_END len=%d\n",
+ bra_option_len);
+ bra_end = 1;
+ break;
+ default:
+ DPRINTF(sc, UPGT_DEBUG_FW, "unknown BRA option len=%d\n",
+ bra_option_len);
+ error = EIO;
+ goto fail;
+ }
+
+ /* jump to next BRA option */
+ offset += sizeof(struct upgt_fw_bra_option) + bra_option_len;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware verified", __func__);
+fail:
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ return (error);
+}
+
+static int
+upgt_bulk_xmit(struct upgt_softc *sc, struct upgt_data *data,
+ usbd_pipe_handle pipeh, uint32_t *size, int flags)
+{
+ usbd_status status;
+
+ mtx_lock(&Giant);
+ status = usbd_bulk_transfer(data->xfer, pipeh,
+ USBD_NO_COPY | flags, UPGT_USB_TIMEOUT, data->buf, size,
+ "upgt_bulk_xmit");
+ if (status != USBD_NORMAL_COMPLETION) {
+ device_printf(sc->sc_dev, "%s: error %s!\n",
+ __func__, usbd_errstr(status));
+ mtx_unlock(&Giant);
+ return (EIO);
+ }
+ mtx_unlock(&Giant);
+
+ return (0);
+}
+
+static int
+upgt_device_reset(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ char init_cmd[] = { 0x7e, 0x7e, 0x7e, 0x7e };
+ int len;
+
+ len = sizeof(init_cmd);
+ bcopy(init_cmd, data_cmd->buf, len);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ device_printf(sc->sc_dev,
+ "could not send device init string!\n");
+ return (EIO);
+ }
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: device initialized\n", __func__);
+ return (0);
+}
+
+static int
+upgt_alloc_tx(struct upgt_softc *sc)
+{
+ int i;
+
+ sc->tx_queued = 0;
+
+ for (i = 0; i < upgt_txbuf; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ data_tx->sc = sc;
+ data_tx->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data_tx->xfer == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate TX xfer!\n");
+ return (ENOMEM);
+ }
+
+ data_tx->buf = usbd_alloc_buffer(data_tx->xfer, MCLBYTES);
+ if (data_tx->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate TX buffer!\n");
+ return (ENOMEM);
+ }
+
+ bzero(data_tx->buf, MCLBYTES);
+ }
+
+ return (0);
+}
+
+static int
+upgt_alloc_rx(struct upgt_softc *sc)
+{
+ struct upgt_data *data_rx = &sc->rx_data;
+
+ data_rx->sc = sc;
+ data_rx->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data_rx->xfer == NULL) {
+ device_printf(sc->sc_dev, "could not allocate RX xfer!\n");
+ return (ENOMEM);
+ }
+
+ data_rx->buf = usbd_alloc_buffer(data_rx->xfer, MCLBYTES);
+ if (data_rx->buf == NULL) {
+ device_printf(sc->sc_dev, "could not allocate RX buffer!\n");
+ return (ENOMEM);
+ }
+
+ bzero(data_rx->buf, MCLBYTES);
+
+ return (0);
+}
+
+static int
+upgt_alloc_cmd(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+
+ data_cmd->sc = sc;
+ data_cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data_cmd->xfer == NULL) {
+ device_printf(sc->sc_dev, "could not allocate RX xfer!\n");
+ return (ENOMEM);
+ }
+
+ data_cmd->buf = usbd_alloc_buffer(data_cmd->xfer, MCLBYTES);
+ if (data_cmd->buf == NULL) {
+ device_printf(sc->sc_dev, "could not allocate RX buffer!\n");
+ return (ENOMEM);
+ }
+
+ bzero(data_cmd->buf, MCLBYTES);
+
+ return (0);
+}
+
+static int
+upgt_detach(device_t dev)
+{
+ struct upgt_softc *sc = device_get_softc(dev);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ if (!device_is_attached(dev))
+ return 0;
+
+ upgt_stop(sc, 1);
+
+ /* abort and close TX / RX pipes */
+ if (sc->sc_tx_pipeh != NULL)
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ if (sc->sc_rx_pipeh != NULL)
+ usbd_close_pipe(sc->sc_rx_pipeh);
+
+ mtx_destroy(&sc->sc_mtx);
+ usb_rem_task(sc->sc_udev, &sc->sc_mcasttask);
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_task_tx);
+ callout_stop(&sc->sc_led_ch);
+ callout_stop(&sc->sc_watchdog_ch);
+
+ /* free xfers */
+ upgt_free_tx(sc);
+ upgt_free_rx(sc);
+ upgt_free_cmd(sc);
+
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+ if_free(ifp);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+
+ return 0;
+}
+
+static void
+upgt_free_rx(struct upgt_softc *sc)
+{
+ struct upgt_data *data_rx = &sc->rx_data;
+
+ if (data_rx->xfer != NULL) {
+ usbd_free_xfer(data_rx->xfer);
+ data_rx->xfer = NULL;
+ }
+
+ data_rx->ni = NULL;
+}
+
+static void
+upgt_free_tx(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < upgt_txbuf; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ if (data_tx->xfer != NULL) {
+ usbd_free_xfer(data_tx->xfer);
+ data_tx->xfer = NULL;
+ }
+
+ data_tx->ni = NULL;
+ }
+}
+
+static void
+upgt_free_cmd(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+
+ if (data_cmd->xfer != NULL) {
+ usbd_free_xfer(data_cmd->xfer);
+ data_cmd->xfer = NULL;
+ }
+}
+
+static device_method_t upgt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, upgt_match),
+ DEVMETHOD(device_attach, upgt_attach),
+ DEVMETHOD(device_detach, upgt_detach),
+
+ { 0, 0 }
+};
+
+static driver_t upgt_driver = {
+ "upgt",
+ upgt_methods,
+ sizeof(struct upgt_softc)
+};
+
+static devclass_t upgt_devclass;
+
+DRIVER_MODULE(if_upgt, uhub, upgt_driver, upgt_devclass, usbd_driver_load, 0);
+MODULE_VERSION(if_upgt, 1);
+MODULE_DEPEND(if_upgt, usb, 1, 1, 1);
+MODULE_DEPEND(if_upgt, wlan, 1, 1, 1);
+MODULE_DEPEND(if_upgt, upgtfw_fw, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/if_upgtvar.h b/sys/legacy/dev/usb/if_upgtvar.h
new file mode 100644
index 0000000..298fb63
--- /dev/null
+++ b/sys/legacy/dev/usb/if_upgtvar.h
@@ -0,0 +1,462 @@
+/* $OpenBSD: if_upgtvar.h,v 1.14 2008/02/02 13:48:44 mglocker Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct upgt_softc;
+
+/*
+ * General values.
+ */
+#define UPGT_IFACE_INDEX 0
+#define UPGT_CONFIG_NO 1
+#define UPGT_USB_TIMEOUT 1000
+#define UPGT_FIRMWARE_TIMEOUT 10
+
+#define UPGT_MEMADDR_FIRMWARE_START 0x00020000 /* 512 bytes large */
+#define UPGT_MEMSIZE_FRAME_HEAD 0x0070
+#define UPGT_MEMSIZE_RX 0x3500
+
+#define UPGT_TX_COUNT 2
+
+/* device flags */
+#define UPGT_DEVICE_ATTACHED (1 << 0)
+
+/* leds */
+#define UPGT_LED_OFF 0
+#define UPGT_LED_ON 1
+#define UPGT_LED_BLINK 2
+
+/*
+ * Firmware.
+ */
+#define UPGT_FW_BLOCK_SIZE 512
+
+#define UPGT_BRA_FWTYPE_SIZE 4
+#define UPGT_BRA_FWTYPE_LM86 "LM86"
+#define UPGT_BRA_FWTYPE_LM87 "LM87"
+enum upgt_fw_type {
+ UPGT_FWTYPE_LM86,
+ UPGT_FWTYPE_LM87
+};
+
+#define UPGT_BRA_TYPE_FW 0x80000001
+#define UPGT_BRA_TYPE_VERSION 0x80000002
+#define UPGT_BRA_TYPE_DEPIF 0x80000003
+#define UPGT_BRA_TYPE_EXPIF 0x80000004
+#define UPGT_BRA_TYPE_DESCR 0x80000101
+#define UPGT_BRA_TYPE_END 0xff0000ff
+struct upgt_fw_bra_option {
+ uint32_t type;
+ uint32_t len;
+ uint8_t data[];
+} __packed;
+
+struct upgt_fw_bra_descr {
+ uint32_t unknown1;
+ uint32_t memaddr_space_start;
+ uint32_t memaddr_space_end;
+ uint32_t unknown2;
+ uint32_t unknown3;
+ uint8_t rates[20];
+} __packed;
+
+#define UPGT_X2_SIGNATURE_SIZE 4
+#define UPGT_X2_SIGNATURE "x2 "
+struct upgt_fw_x2_header {
+ uint8_t signature[4];
+ uint32_t startaddr;
+ uint32_t len;
+ uint32_t crc;
+} __packed;
+
+/*
+ * EEPROM.
+ */
+#define UPGT_EEPROM_SIZE 8192
+#define UPGT_EEPROM_BLOCK_SIZE 1020
+
+struct upgt_eeprom_header {
+ /* 14 bytes */
+ uint32_t magic;
+ uint16_t pad1;
+ uint16_t preamble_len;
+ uint32_t pad2;
+ /* data */
+} __packed;
+
+#define UPGT_EEPROM_TYPE_END 0x0000
+#define UPGT_EEPROM_TYPE_NAME 0x0001
+#define UPGT_EEPROM_TYPE_SERIAL 0x0003
+#define UPGT_EEPROM_TYPE_MAC 0x0101
+#define UPGT_EEPROM_TYPE_HWRX 0x1001
+#define UPGT_EEPROM_TYPE_CHIP 0x1002
+#define UPGT_EEPROM_TYPE_FREQ3 0x1903
+#define UPGT_EEPROM_TYPE_FREQ4 0x1904
+#define UPGT_EEPROM_TYPE_FREQ5 0x1905
+#define UPGT_EEPROM_TYPE_FREQ6 0x1906
+#define UPGT_EEPROM_TYPE_OFF 0xffff
+struct upgt_eeprom_option {
+ uint16_t len;
+ uint16_t type;
+ uint8_t data[];
+ /* data */
+} __packed;
+
+#define UPGT_EEPROM_RX_CONST 0x88
+struct upgt_eeprom_option_hwrx {
+ uint32_t pad1;
+ uint8_t rxfilter;
+ uint8_t pad2[15];
+} __packed;
+
+struct upgt_eeprom_freq3_header {
+ uint8_t flags;
+ uint8_t elements;
+} __packed;
+
+struct upgt_eeprom_freq4_header {
+ uint8_t flags;
+ uint8_t elements;
+ uint8_t settings;
+ uint8_t type;
+} __packed;
+
+struct upgt_eeprom_freq4_1 {
+ uint16_t freq;
+ uint8_t data[50];
+} __packed;
+
+struct upgt_eeprom_freq4_2 {
+ uint16_t head;
+ uint8_t subtails[4];
+ uint8_t tail;
+} __packed;
+
+/*
+ * LMAC protocol.
+ */
+struct upgt_lmac_mem {
+ uint32_t addr;
+ uint32_t chksum;
+} __packed;
+
+#define UPGT_H1_FLAGS_TX_MGMT 0x00 /* for TX: mgmt frame */
+#define UPGT_H1_FLAGS_TX_NO_CALLBACK 0x01 /* for TX: no USB callback */
+#define UPGT_H1_FLAGS_TX_DATA 0x10 /* for TX: data frame */
+#define UPGT_H1_TYPE_RX_DATA 0x00 /* 802.11 RX data frame */
+#define UPGT_H1_TYPE_RX_DATA_MGMT 0x04 /* 802.11 RX mgmt frame */
+#define UPGT_H1_TYPE_TX_DATA 0x40 /* 802.11 TX data frame */
+#define UPGT_H1_TYPE_CTRL 0x80 /* control frame */
+struct upgt_lmac_h1 {
+ /* 4 bytes */
+ uint8_t flags;
+ uint8_t type;
+ uint16_t len;
+} __packed;
+
+#define UPGT_H2_TYPE_TX_ACK_NO 0x0000
+#define UPGT_H2_TYPE_TX_ACK_YES 0x0001
+#define UPGT_H2_TYPE_MACFILTER 0x0000
+#define UPGT_H2_TYPE_CHANNEL 0x0001
+#define UPGT_H2_TYPE_TX_DONE 0x0008
+#define UPGT_H2_TYPE_STATS 0x000a
+#define UPGT_H2_TYPE_EEPROM 0x000c
+#define UPGT_H2_TYPE_LED 0x000d
+#define UPGT_H2_FLAGS_TX_ACK_NO 0x0101
+#define UPGT_H2_FLAGS_TX_ACK_YES 0x0707
+struct upgt_lmac_h2 {
+ /* 8 bytes */
+ uint32_t reqid;
+ uint16_t type;
+ uint16_t flags;
+} __packed;
+
+struct upgt_lmac_header {
+ /* 12 bytes */
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+} __packed;
+
+struct upgt_lmac_eeprom {
+ /* 16 bytes */
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t offset;
+ uint16_t len;
+ /* data */
+} __packed;
+
+#define UPGT_FILTER_TYPE_NONE 0x0000
+#define UPGT_FILTER_TYPE_STA 0x0001
+#define UPGT_FILTER_TYPE_IBSS 0x0002
+#define UPGT_FILTER_TYPE_HOSTAP 0x0004
+#define UPGT_FILTER_TYPE_MONITOR 0x0010
+#define UPGT_FILTER_TYPE_RESET 0x0020
+#define UPGT_FILTER_UNKNOWN1 0x0002
+#define UPGT_FILTER_UNKNOWN2 0x0ca8
+#define UPGT_FILTER_UNKNOWN3 0xffff
+#define UPGT_FILTER_MONITOR_UNKNOWN1 0x0000
+#define UPGT_FILTER_MONITOR_UNKNOWN2 0x0000
+#define UPGT_FILTER_MONITOR_UNKNOWN3 0x0000
+struct upgt_lmac_filter {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ /* 32 bytes */
+ uint16_t type;
+ uint8_t dst[IEEE80211_ADDR_LEN];
+ uint8_t src[IEEE80211_ADDR_LEN];
+ uint16_t unknown1;
+ uint32_t rxaddr;
+ uint16_t unknown2;
+ uint32_t rxhw;
+ uint16_t unknown3;
+ uint32_t unknown4;
+} __packed;
+
+/* frequence 3 data */
+struct upgt_lmac_freq3 {
+ uint16_t freq;
+ uint8_t data[6];
+} __packed;
+
+/* frequence 4 data */
+struct upgt_lmac_freq4 {
+ struct upgt_eeprom_freq4_2 cmd;
+ uint8_t pad;
+};
+
+/* frequence 6 data */
+struct upgt_lmac_freq6 {
+ uint16_t freq;
+ uint8_t data[8];
+} __packed;
+
+#define UPGT_CHANNEL_UNKNOWN1 0x0001
+#define UPGT_CHANNEL_UNKNOWN2 0x0000
+#define UPGT_CHANNEL_UNKNOWN3 0x48
+struct upgt_lmac_channel {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ /* 112 bytes */
+ uint16_t unknown1;
+ uint16_t unknown2;
+ uint8_t pad1[20];
+ struct upgt_lmac_freq6 freq6;
+ uint8_t settings;
+ uint8_t unknown3;
+ uint8_t freq3_1[4];
+ struct upgt_lmac_freq4 freq4[8];
+ uint8_t freq3_2[4];
+ uint32_t pad2;
+} __packed;
+
+#define UPGT_LED_MODE_SET 0x0003
+#define UPGT_LED_ACTION_OFF 0x0002
+#define UPGT_LED_ACTION_ON 0x0003
+#define UPGT_LED_ACTION_TMP_DUR 100 /* ms */
+struct upgt_lmac_led {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t mode;
+ uint16_t action_fix;
+ uint16_t action_tmp;
+ uint16_t action_tmp_dur;
+} __packed;
+
+struct upgt_lmac_stats {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint8_t data[76];
+} __packed;
+
+struct upgt_lmac_rx_desc {
+ struct upgt_lmac_h1 header1;
+ /* 16 bytes */
+ uint16_t freq;
+ uint8_t unknown1;
+ uint8_t rate;
+ uint8_t rssi;
+ uint8_t pad;
+ uint16_t unknown2;
+ uint32_t timestamp;
+ uint32_t unknown3;
+ uint8_t data[];
+} __packed;
+
+#define UPGT_TX_DESC_KEY_EXISTS 0x01
+struct upgt_lmac_tx_desc_wep {
+ uint8_t key_exists;
+ uint8_t key_len;
+ uint8_t key_val[16];
+} __packed;
+
+#define UPGT_TX_DESC_TYPE_BEACON 0x00000000
+#define UPGT_TX_DESC_TYPE_PROBE 0x00000001
+#define UPGT_TX_DESC_TYPE_MGMT 0x00000002
+#define UPGT_TX_DESC_TYPE_DATA 0x00000004
+#define UPGT_TX_DESC_PAD3_SIZE 2
+struct upgt_lmac_tx_desc {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint8_t rates[8];
+ uint16_t pad1;
+ struct upgt_lmac_tx_desc_wep wep_key;
+ uint32_t type;
+ uint32_t pad2;
+ uint32_t unknown1;
+ uint32_t unknown2;
+ uint8_t pad3[2];
+ /* 802.11 frame data */
+} __packed;
+
+#define UPGT_TX_DONE_DESC_STATUS_OK 0x0001
+struct upgt_lmac_tx_done_desc {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t status;
+ uint16_t rssi;
+ uint16_t seq;
+ uint16_t unknown;
+} __packed;
+
+/*
+ * USB xfers.
+ */
+struct upgt_data {
+ struct upgt_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ uint32_t addr;
+ uint8_t use;
+};
+
+/*
+ * Prism memory.
+ */
+struct upgt_memory_page {
+ uint8_t used;
+ uint32_t addr;
+} __packed;
+
+#define UPGT_MEMORY_MAX_PAGES 8
+struct upgt_memory {
+ uint8_t pages;
+ struct upgt_memory_page page[UPGT_MEMORY_MAX_PAGES];
+} __packed;
+
+/*
+ * BPF
+ */
+struct upgt_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+} __packed;
+
+#define UPGT_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct upgt_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed;
+
+#define UPGT_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct upgt_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define UPGT_VAP(vap) ((struct upgt_vap *)(vap))
+
+struct upgt_softc {
+ device_t sc_dev;
+ struct ifnet *sc_ifp;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+ struct mtx sc_mtx;
+ int sc_if_flags;
+ int sc_debug;
+
+ struct usb_task sc_mcasttask;
+ struct usb_task sc_task;
+ struct usb_task sc_scantask;
+#define UPGT_SET_CHANNEL 2
+ int sc_scan_action;
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int sc_led_blink;
+ struct callout sc_led_ch;
+ uint8_t sc_cur_rateset[8];
+
+ /* watchdog */
+ int sc_tx_timer;
+ struct callout sc_watchdog_ch;
+
+ /* Firmware. */
+ int sc_fw_type;
+ /* memory addresses on device */
+ uint32_t sc_memaddr_frame_start;
+ uint32_t sc_memaddr_frame_end;
+ uint32_t sc_memaddr_rx_start;
+ struct upgt_memory sc_memory;
+
+ /* data which we found in the EEPROM */
+ uint8_t sc_eeprom[UPGT_EEPROM_SIZE];
+ uint16_t sc_eeprom_hwrx;
+ struct upgt_lmac_freq3 sc_eeprom_freq3[IEEE80211_CHAN_MAX];
+ struct upgt_lmac_freq4 sc_eeprom_freq4[IEEE80211_CHAN_MAX][8];
+ struct upgt_lmac_freq6 sc_eeprom_freq6[IEEE80211_CHAN_MAX];
+ uint8_t sc_eeprom_freq6_settings;
+
+ /* RX/TX */
+ int sc_rx_no;
+ int sc_tx_no;
+ usbd_pipe_handle sc_rx_pipeh;
+ usbd_pipe_handle sc_tx_pipeh;
+ struct upgt_data tx_data[UPGT_TX_COUNT];
+ struct upgt_data rx_data;
+ struct upgt_data cmd_data;
+ int tx_queued;
+ struct usb_task sc_task_tx;
+
+ /* BPF */
+ struct upgt_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+
+ struct upgt_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#define UPGT_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define UPGT_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
diff --git a/sys/legacy/dev/usb/if_ural.c b/sys/legacy/dev/usb/if_ural.c
new file mode 100644
index 0000000..4595761
--- /dev/null
+++ b/sys/legacy/dev/usb/if_ural.c
@@ -0,0 +1,2505 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2500USB chipset driver
+ * http://www.ralinktech.com/
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_regdomain.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/if_uralreg.h>
+#include <dev/usb/if_uralvar.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) do { if (uraldebug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (uraldebug >= (n)) printf x; } while (0)
+int uraldebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural");
+SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RW, &uraldebug, 0,
+ "ural debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+#define URAL_RSSI(rssi) \
+ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \
+ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0)
+
+/* various supported device vendors/products */
+static const struct usb_devno ural_devs[] = {
+ { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G },
+ { USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570 },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050 },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051 },
+ { USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU },
+ { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122 },
+ { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG },
+ { USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G },
+ { USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254 },
+ { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G },
+ { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP },
+ { USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54 },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570 },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2 },
+ { USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3 },
+ { USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902 },
+ { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570 },
+ { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2 },
+ { USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3 },
+ { USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G },
+ { USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG },
+ { USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R},
+ { USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570 },
+ { USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570 },
+ { USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570 }
+};
+
+MODULE_DEPEND(ural, wlan, 1, 1, 1);
+MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1);
+MODULE_DEPEND(ural, usb, 1, 1, 1);
+
+static struct ieee80211vap *ural_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ural_vap_delete(struct ieee80211vap *);
+static int ural_alloc_tx_list(struct ural_softc *);
+static void ural_free_tx_list(struct ural_softc *);
+static int ural_alloc_rx_list(struct ural_softc *);
+static void ural_free_rx_list(struct ural_softc *);
+static void ural_task(void *);
+static void ural_scantask(void *);
+static int ural_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static void ural_txeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+static void ural_rxeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+static void ural_setup_tx_desc(struct ural_softc *,
+ struct ural_tx_desc *, uint32_t, int, int);
+static int ural_tx_bcn(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int ural_tx_mgt(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int ural_tx_data(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static void ural_start(struct ifnet *);
+static void ural_watchdog(void *);
+static int ural_ioctl(struct ifnet *, u_long, caddr_t);
+static void ural_set_testmode(struct ural_softc *);
+static void ural_eeprom_read(struct ural_softc *, uint16_t, void *,
+ int);
+static uint16_t ural_read(struct ural_softc *, uint16_t);
+static void ural_read_multi(struct ural_softc *, uint16_t, void *,
+ int);
+static void ural_write(struct ural_softc *, uint16_t, uint16_t);
+static void ural_write_multi(struct ural_softc *, uint16_t, void *,
+ int) __unused;
+static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t);
+static uint8_t ural_bbp_read(struct ural_softc *, uint8_t);
+static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t);
+static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ural_newassoc(struct ieee80211_node *, int);
+static void ural_scan_start(struct ieee80211com *);
+static void ural_scan_end(struct ieee80211com *);
+static void ural_set_channel(struct ieee80211com *);
+static void ural_set_chan(struct ural_softc *,
+ struct ieee80211_channel *);
+static void ural_disable_rf_tune(struct ural_softc *);
+static void ural_enable_tsf_sync(struct ural_softc *);
+static void ural_update_slot(struct ifnet *);
+static void ural_set_txpreamble(struct ural_softc *);
+static void ural_set_basicrates(struct ural_softc *,
+ const struct ieee80211_channel *);
+static void ural_set_bssid(struct ural_softc *, const uint8_t *);
+static void ural_set_macaddr(struct ural_softc *, uint8_t *);
+static void ural_update_promisc(struct ural_softc *);
+static const char *ural_get_rf(int);
+static void ural_read_eeprom(struct ural_softc *);
+static int ural_bbp_init(struct ural_softc *);
+static void ural_set_txantenna(struct ural_softc *, int);
+static void ural_set_rxantenna(struct ural_softc *, int);
+static void ural_init_locked(struct ural_softc *);
+static void ural_init(void *);
+static void ural_stop(void *);
+static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void ural_amrr_start(struct ural_softc *,
+ struct ieee80211_node *);
+static void ural_amrr_timeout(void *);
+static void ural_amrr_update(usbd_xfer_handle, usbd_private_handle,
+ usbd_status status);
+
+/*
+ * Default values for MAC registers; values taken from the reference driver.
+ */
+static const struct {
+ uint16_t reg;
+ uint16_t val;
+} ural_def_mac[] = {
+ { RAL_TXRX_CSR5, 0x8c8d },
+ { RAL_TXRX_CSR6, 0x8b8a },
+ { RAL_TXRX_CSR7, 0x8687 },
+ { RAL_TXRX_CSR8, 0x0085 },
+ { RAL_MAC_CSR13, 0x1111 },
+ { RAL_MAC_CSR14, 0x1e11 },
+ { RAL_TXRX_CSR21, 0xe78f },
+ { RAL_MAC_CSR9, 0xff1d },
+ { RAL_MAC_CSR11, 0x0002 },
+ { RAL_MAC_CSR22, 0x0053 },
+ { RAL_MAC_CSR15, 0x0000 },
+ { RAL_MAC_CSR8, 0x0780 },
+ { RAL_TXRX_CSR19, 0x0000 },
+ { RAL_TXRX_CSR18, 0x005a },
+ { RAL_PHY_CSR2, 0x0000 },
+ { RAL_TXRX_CSR0, 0x1ec0 },
+ { RAL_PHY_CSR4, 0x000f }
+};
+
+/*
+ * Default values for BBP registers; values taken from the reference driver.
+ */
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} ural_def_bbp[] = {
+ { 3, 0x02 },
+ { 4, 0x19 },
+ { 14, 0x1c },
+ { 15, 0x30 },
+ { 16, 0xac },
+ { 17, 0x48 },
+ { 18, 0x18 },
+ { 19, 0xff },
+ { 20, 0x1e },
+ { 21, 0x08 },
+ { 22, 0x08 },
+ { 23, 0x08 },
+ { 24, 0x80 },
+ { 25, 0x50 },
+ { 26, 0x08 },
+ { 27, 0x23 },
+ { 30, 0x10 },
+ { 31, 0x2b },
+ { 32, 0xb9 },
+ { 34, 0x12 },
+ { 35, 0x50 },
+ { 39, 0xc4 },
+ { 40, 0x02 },
+ { 41, 0x60 },
+ { 53, 0x10 },
+ { 54, 0x18 },
+ { 56, 0x08 },
+ { 57, 0x10 },
+ { 58, 0x08 },
+ { 61, 0x60 },
+ { 62, 0x10 },
+ { 75, 0xff }
+};
+
+/*
+ * Default values for RF register R2 indexed by channel numbers.
+ */
+static const uint32_t ural_rf2522_r2[] = {
+ 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814,
+ 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e
+};
+
+static const uint32_t ural_rf2523_r2[] = {
+ 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
+ 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
+};
+
+static const uint32_t ural_rf2524_r2[] = {
+ 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
+ 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
+};
+
+static const uint32_t ural_rf2525_r2[] = {
+ 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d,
+ 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346
+};
+
+static const uint32_t ural_rf2525_hi_r2[] = {
+ 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345,
+ 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e
+};
+
+static const uint32_t ural_rf2525e_r2[] = {
+ 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463,
+ 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b
+};
+
+static const uint32_t ural_rf2526_hi_r2[] = {
+ 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d,
+ 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241
+};
+
+static const uint32_t ural_rf2526_r2[] = {
+ 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229,
+ 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d
+};
+
+/*
+ * For dual-band RF, RF registers R1 and R4 also depend on channel number;
+ * values taken from the reference driver.
+ */
+static const struct {
+ uint8_t chan;
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r4;
+} ural_rf5222[] = {
+ { 1, 0x08808, 0x0044d, 0x00282 },
+ { 2, 0x08808, 0x0044e, 0x00282 },
+ { 3, 0x08808, 0x0044f, 0x00282 },
+ { 4, 0x08808, 0x00460, 0x00282 },
+ { 5, 0x08808, 0x00461, 0x00282 },
+ { 6, 0x08808, 0x00462, 0x00282 },
+ { 7, 0x08808, 0x00463, 0x00282 },
+ { 8, 0x08808, 0x00464, 0x00282 },
+ { 9, 0x08808, 0x00465, 0x00282 },
+ { 10, 0x08808, 0x00466, 0x00282 },
+ { 11, 0x08808, 0x00467, 0x00282 },
+ { 12, 0x08808, 0x00468, 0x00282 },
+ { 13, 0x08808, 0x00469, 0x00282 },
+ { 14, 0x08808, 0x0046b, 0x00286 },
+
+ { 36, 0x08804, 0x06225, 0x00287 },
+ { 40, 0x08804, 0x06226, 0x00287 },
+ { 44, 0x08804, 0x06227, 0x00287 },
+ { 48, 0x08804, 0x06228, 0x00287 },
+ { 52, 0x08804, 0x06229, 0x00287 },
+ { 56, 0x08804, 0x0622a, 0x00287 },
+ { 60, 0x08804, 0x0622b, 0x00287 },
+ { 64, 0x08804, 0x0622c, 0x00287 },
+
+ { 100, 0x08804, 0x02200, 0x00283 },
+ { 104, 0x08804, 0x02201, 0x00283 },
+ { 108, 0x08804, 0x02202, 0x00283 },
+ { 112, 0x08804, 0x02203, 0x00283 },
+ { 116, 0x08804, 0x02204, 0x00283 },
+ { 120, 0x08804, 0x02205, 0x00283 },
+ { 124, 0x08804, 0x02206, 0x00283 },
+ { 128, 0x08804, 0x02207, 0x00283 },
+ { 132, 0x08804, 0x02208, 0x00283 },
+ { 136, 0x08804, 0x02209, 0x00283 },
+ { 140, 0x08804, 0x0220a, 0x00283 },
+
+ { 149, 0x08808, 0x02429, 0x00281 },
+ { 153, 0x08808, 0x0242b, 0x00281 },
+ { 157, 0x08808, 0x0242d, 0x00281 },
+ { 161, 0x08808, 0x0242f, 0x00281 }
+};
+
+static device_probe_t ural_match;
+static device_attach_t ural_attach;
+static device_detach_t ural_detach;
+
+static device_method_t ural_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ural_match),
+ DEVMETHOD(device_attach, ural_attach),
+ DEVMETHOD(device_detach, ural_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ural_driver = {
+ "ural",
+ ural_methods,
+ sizeof(struct ural_softc)
+};
+
+static devclass_t ural_devclass;
+
+DRIVER_MODULE(ural, uhub, ural_driver, ural_devclass, usbd_driver_load, 0);
+
+static int
+ural_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return UMATCH_NONE;
+
+ return (usb_lookup(ural_devs, uaa->vendor, uaa->product) != NULL) ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+}
+
+static int
+ural_attach(device_t self)
+{
+ struct ural_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct ifnet *ifp;
+ struct ieee80211com *ic;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status error;
+ int i;
+ uint8_t bands;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+
+ if (usbd_set_config_no(sc->sc_udev, RAL_CONFIG_NO, 0) != 0) {
+ device_printf(self, "could not set configuration no\n");
+ return ENXIO;
+ }
+
+ /* get the first interface handle */
+ error = usbd_device2interface_handle(sc->sc_udev, RAL_IFACE_INDEX,
+ &sc->sc_iface);
+ if (error != 0) {
+ device_printf(self, "could not get interface handle\n");
+ return ENXIO;
+ }
+
+ /*
+ * Find endpoints.
+ */
+ id = usbd_get_interface_descriptor(sc->sc_iface);
+
+ sc->sc_rx_no = sc->sc_tx_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(self, "no endpoint descriptor for %d\n",
+ i);
+ return ENXIO;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_rx_no = ed->bEndpointAddress;
+ else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_tx_no = ed->bEndpointAddress;
+ }
+ if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
+ device_printf(self, "missing endpoint\n");
+ return ENXIO;
+ }
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
+ return ENXIO;
+ }
+ ic = ifp->if_l2com;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+
+ usb_init_task(&sc->sc_task, ural_task, sc);
+ usb_init_task(&sc->sc_scantask, ural_scantask, sc);
+ callout_init(&sc->watchdog_ch, 0);
+
+ /* retrieve RT2570 rev. no */
+ sc->asic_rev = ural_read(sc, RAL_MAC_CSR0);
+
+ /* retrieve MAC address and various other things from EEPROM */
+ ural_read_eeprom(sc);
+
+ device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
+ sc->asic_rev, ural_get_rf(sc->rf_rev));
+
+ ifp->if_softc = sc;
+ if_initname(ifp, "ural", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT; /* USB stack is still under Giant lock */
+ ifp->if_init = ural_init;
+ ifp->if_ioctl = ural_ioctl;
+ ifp->if_start = ural_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode supported */
+ | IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ if (sc->rf_rev == RAL_RF_5222)
+ setbit(&bands, IEEE80211_MODE_11A);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_newassoc = ural_newassoc;
+ ic->ic_raw_xmit = ural_raw_xmit;
+ ic->ic_node_alloc = ural_node_alloc;
+ ic->ic_scan_start = ural_scan_start;
+ ic->ic_scan_end = ural_scan_end;
+ ic->ic_set_channel = ural_set_channel;
+
+ ic->ic_vap_create = ural_vap_create;
+ ic->ic_vap_delete = ural_vap_delete;
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
+
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT);
+
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return 0;
+}
+
+static int
+ural_detach(device_t self)
+{
+ struct ural_softc *sc = device_get_softc(self);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ ural_stop(sc);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+ callout_stop(&sc->watchdog_ch);
+
+ if (sc->amrr_xfer != NULL) {
+ usbd_free_xfer(sc->amrr_xfer);
+ sc->amrr_xfer = NULL;
+ }
+
+ if (sc->sc_rx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_rx_pipeh);
+ usbd_close_pipe(sc->sc_rx_pipeh);
+ }
+
+ if (sc->sc_tx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ }
+
+ ural_free_rx_list(sc);
+ ural_free_tx_list(sc);
+
+ if_free(ifp);
+ mtx_destroy(&sc->sc_mtx);
+
+ return 0;
+}
+
+static struct ieee80211vap *
+ural_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ural_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (uvp == NULL)
+ return NULL;
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ural_newstate;
+
+ callout_init(&uvp->amrr_ch, 0);
+ ieee80211_amrr_init(&uvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+ural_vap_delete(struct ieee80211vap *vap)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+
+ callout_stop(&uvp->amrr_ch);
+ ieee80211_amrr_cleanup(&uvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static int
+ural_alloc_tx_list(struct ural_softc *sc)
+{
+ struct ural_tx_data *data;
+ int i, error;
+
+ sc->tx_queued = sc->tx_cur = 0;
+
+ for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate tx xfer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ data->buf = usbd_alloc_buffer(data->xfer,
+ RAL_TX_DESC_SIZE + MCLBYTES);
+ if (data->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate tx buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail: ural_free_tx_list(sc);
+ return error;
+}
+
+static void
+ural_free_tx_list(struct ural_softc *sc)
+{
+ struct ural_tx_data *data;
+ int i;
+
+ for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->xfer != NULL) {
+ usbd_free_xfer(data->xfer);
+ data->xfer = NULL;
+ }
+
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static int
+ural_alloc_rx_list(struct ural_softc *sc)
+{
+ struct ural_rx_data *data;
+ int i, error;
+
+ for (i = 0; i < RAL_RX_LIST_COUNT; i++) {
+ data = &sc->rx_data[i];
+
+ data->sc = sc;
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx xfer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (data->m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ data->buf = mtod(data->m, uint8_t *);
+ }
+
+ return 0;
+
+fail: ural_free_rx_list(sc);
+ return error;
+}
+
+static void
+ural_free_rx_list(struct ural_softc *sc)
+{
+ struct ural_rx_data *data;
+ int i;
+
+ for (i = 0; i < RAL_RX_LIST_COUNT; i++) {
+ data = &sc->rx_data[i];
+
+ if (data->xfer != NULL) {
+ usbd_free_xfer(data->xfer);
+ data->xfer = NULL;
+ }
+
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ }
+}
+
+static void
+ural_task(void *xarg)
+{
+ struct ural_softc *sc = xarg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ural_vap *uvp = URAL_VAP(vap);
+ const struct ieee80211_txparam *tp;
+ enum ieee80211_state ostate;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ ostate = vap->iv_state;
+
+ RAL_LOCK(sc);
+ switch (sc->sc_state) {
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_RUN) {
+ /* abort TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+
+ /* force tx led to stop blinking */
+ ural_write(sc, RAL_MAC_CSR20, 0);
+ }
+ break;
+
+ case IEEE80211_S_RUN:
+ ni = vap->iv_bss;
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ ural_update_slot(ic->ic_ifp);
+ ural_set_txpreamble(sc);
+ ural_set_basicrates(sc, ic->ic_bsschan);
+ ural_set_bssid(sc, ni->ni_bssid);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ m = ieee80211_beacon_alloc(ni, &uvp->bo);
+ if (m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate beacon\n");
+ return;
+ }
+
+ if (ural_tx_bcn(sc, m, ni) != 0) {
+ device_printf(sc->sc_dev,
+ "could not send beacon\n");
+ return;
+ }
+ }
+
+ /* make tx led blink on tx (controlled by ASIC) */
+ ural_write(sc, RAL_MAC_CSR20, 1);
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
+ ural_enable_tsf_sync(sc);
+
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ ural_amrr_start(sc, ni);
+
+ break;
+
+ default:
+ break;
+ }
+
+ RAL_UNLOCK(sc);
+
+ IEEE80211_LOCK(ic);
+ uvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+ural_scantask(void *arg)
+{
+ struct ural_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ RAL_LOCK(sc);
+ if (sc->sc_scan_action == URAL_SCAN_START) {
+ /* abort TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+ ural_set_bssid(sc, ifp->if_broadcastaddr);
+ } else if (sc->sc_scan_action == URAL_SET_CHANNEL) {
+ mtx_lock(&Giant);
+ ural_set_chan(sc, ic->ic_curchan);
+ mtx_unlock(&Giant);
+ } else {
+ ural_enable_tsf_sync(sc);
+ /* XXX keep local copy */
+ ural_set_bssid(sc, vap->iv_bss->ni_bssid);
+ }
+ RAL_UNLOCK(sc);
+}
+
+static int
+ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ callout_stop(&uvp->amrr_ch);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ if (nstate == IEEE80211_S_INIT) {
+ uvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
+ usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
+ return EINPROGRESS;
+ }
+}
+
+#define RAL_RXTX_TURNAROUND 5 /* us */
+
+static void
+ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ural_tx_data *data = priv;
+ struct ural_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if (data->m->m_flags & M_TXCB)
+ ieee80211_process_callback(data->ni, data->m,
+ status == USBD_NORMAL_COMPLETION ? 0 : ETIMEDOUT);
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
+
+ ifp->if_oerrors++;
+ /* XXX mbuf leak? */
+ return;
+ }
+
+ m_freem(data->m);
+ data->m = NULL;
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+
+ sc->tx_queued--;
+ ifp->if_opackets++;
+
+ DPRINTFN(10, ("tx done\n"));
+
+ sc->sc_tx_timer = 0;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ural_start(ifp);
+}
+
+static void
+ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ural_rx_data *data = priv;
+ struct ural_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ural_rx_desc *desc;
+ struct ieee80211_node *ni;
+ struct mbuf *mnew, *m;
+ int len, rssi;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
+ goto skip;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ if (len < RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN) {
+ DPRINTF(("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev),
+ len));
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ /* rx descriptor is located at the end */
+ desc = (struct ural_rx_desc *)(data->buf + len - RAL_RX_DESC_SIZE);
+
+ if ((le32toh(desc->flags) & RAL_RX_PHY_ERROR) ||
+ (le32toh(desc->flags) & RAL_RX_CRC_ERROR)) {
+ /*
+ * This should not happen since we did not request to receive
+ * those frames when we filled RAL_TXRX_CSR2.
+ */
+ DPRINTFN(5, ("PHY or CRC error\n"));
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (mnew == NULL) {
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ m = data->m;
+ data->m = mnew;
+ data->buf = mtod(data->m, uint8_t *);
+
+ /* finalize mbuf */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff;
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct ural_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ (desc->flags & htole32(RAL_RX_OFDM)) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_antenna = sc->rx_ant;
+ tap->wr_antsignal = URAL_RSSI(desc->rssi);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+
+ /* Strip trailing 802.11 MAC FCS. */
+ m_adj(m, -IEEE80211_CRC_LEN);
+
+ rssi = URAL_RSSI(desc->rssi);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, RAL_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, RAL_NOISE_FLOOR, 0);
+
+ DPRINTFN(15, ("rx done\n"));
+
+skip: /* setup a new transfer */
+ usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data, data->buf, MCLBYTES,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ural_rxeof);
+ usbd_transfer(xfer);
+}
+
+static uint8_t
+ural_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+
+static void
+ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
+ uint32_t flags, int len, int rate)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t plcp_length;
+ int remainder;
+
+ desc->flags = htole32(flags);
+ desc->flags |= htole32(RAL_TX_NEWSEQ);
+ desc->flags |= htole32(len << 16);
+
+ desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5));
+ desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame)));
+
+ /* setup PLCP fields */
+ desc->plcp_signal = ural_plcp_signal(rate);
+ desc->plcp_service = 4;
+
+ len += IEEE80211_CRC_LEN;
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
+ desc->flags |= htole32(RAL_TX_OFDM);
+
+ plcp_length = len & 0xfff;
+ desc->plcp_length_hi = plcp_length >> 6;
+ desc->plcp_length_lo = plcp_length & 0x3f;
+ } else {
+ plcp_length = (16 * len + rate - 1) / rate;
+ if (rate == 22) {
+ remainder = (16 * len) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= RAL_PLCP_LENGEXT;
+ }
+ desc->plcp_length_hi = plcp_length >> 8;
+ desc->plcp_length_lo = plcp_length & 0xff;
+
+ if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->plcp_signal |= 0x08;
+ }
+
+ desc->iv = 0;
+ desc->eiv = 0;
+}
+
+#define RAL_TX_TIMEOUT 5000
+
+static int
+ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_txparam *tp;
+ struct ural_tx_desc *desc;
+ usbd_xfer_handle xfer;
+ uint8_t cmd;
+ usbd_status error;
+ uint8_t *buf;
+ int xferlen;
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == NULL)
+ return ENOMEM;
+
+ /* xfer length needs to be a multiple of two! */
+ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;
+
+ buf = usbd_alloc_buffer(xfer, xferlen);
+ if (buf == NULL) {
+ usbd_free_xfer(xfer);
+ return ENOMEM;
+ }
+
+ cmd = 0;
+ usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, &cmd, sizeof cmd,
+ USBD_FORCE_SHORT_XFER, RAL_TX_TIMEOUT, NULL);
+
+ error = usbd_sync_transfer(xfer);
+ if (error != 0) {
+ usbd_free_xfer(xfer);
+ return error;
+ }
+
+ desc = (struct ural_tx_desc *)buf;
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, buf + RAL_TX_DESC_SIZE);
+ ural_setup_tx_desc(sc, desc, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP,
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ DPRINTFN(10, ("sending beacon frame len=%u rate=%u xfer len=%u\n",
+ m0->m_pkthdr.len, tp->mgmtrate, xferlen));
+
+ usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, buf, xferlen,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, NULL);
+
+ error = usbd_sync_transfer(xfer);
+ usbd_free_xfer(xfer);
+
+ return error;
+}
+
+static int
+ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ const struct ieee80211_txparam *tp;
+ struct ural_tx_desc *desc;
+ struct ural_tx_data *data;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ uint32_t flags;
+ uint16_t dur;
+ usbd_status error;
+ int xferlen;
+
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct ural_tx_desc *)data->buf;
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ data->m = m0;
+ data->ni = ni;
+
+ flags = 0;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RAL_TX_ACK;
+
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT &&
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ flags |= RAL_TX_TIMESTAMP;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = tp->mgmtrate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
+ ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, tp->mgmtrate);
+
+ /* align end on a 2-bytes boundary */
+ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;
+
+ /*
+ * No space left in the last URB to store the extra 2 bytes, force
+ * sending of another URB.
+ */
+ if ((xferlen % 64) == 0)
+ xferlen += 2;
+
+ DPRINTFN(10, ("sending mgt frame len=%u rate=%u xfer len=%u\n",
+ m0->m_pkthdr.len, tp->mgmtrate, xferlen));
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT,
+ ural_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ m_freem(m0);
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+ural_sendprot(struct ural_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct ural_tx_desc *desc;
+ struct ural_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+ usbd_status error;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RAL_TX_RETRY(7);
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RAL_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct ural_tx_desc *)data->buf;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ m_copydata(mprot, 0, mprot->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
+ ural_setup_tx_desc(sc, desc, flags, mprot->m_pkthdr.len, protrate);
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ /* NB: no roundup necessary */
+ RAL_TX_DESC_SIZE + mprot->m_pkthdr.len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ural_tx_desc *desc;
+ struct ural_tx_data *data;
+ uint32_t flags;
+ usbd_status error;
+ int xferlen, rate;
+
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct ural_tx_desc *)data->buf;
+
+ rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
+ /* XXX validate */
+ if (rate == 0) {
+ m_freem(m0);
+ return EINVAL;
+ }
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RAL_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = ural_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ data->m = m0;
+ data->ni = ni;
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
+ /* XXX need to setup descriptor ourself */
+ ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);
+
+ /* align end on a 2-bytes boundary */
+ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;
+
+ /*
+ * No space left in the last URB to store the extra 2 bytes, force
+ * sending of another URB.
+ */
+ if ((xferlen % 64) == 0)
+ xferlen += 2;
+
+ DPRINTFN(10, ("sending raw frame len=%u rate=%u xfer len=%u\n",
+ m0->m_pkthdr.len, rate, xferlen));
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT,
+ ural_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ m_freem(m0);
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ural_tx_desc *desc;
+ struct ural_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ uint32_t flags = 0;
+ uint16_t dur;
+ usbd_status error;
+ int xferlen, rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else
+ rate = ni->ni_txrate;
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = ural_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+ }
+
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct ural_tx_desc *)data->buf;
+
+ data->m = m0;
+ data->ni = ni;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RAL_TX_ACK;
+ flags |= RAL_TX_RETRY(7);
+
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
+ ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);
+
+ /* align end on a 2-bytes boundary */
+ xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;
+
+ /*
+ * No space left in the last URB to store the extra 2 bytes, force
+ * sending of another URB.
+ */
+ if ((xferlen % 64) == 0)
+ xferlen += 2;
+
+ DPRINTFN(10, ("sending data frame len=%u rate=%u xfer len=%u\n",
+ m0->m_pkthdr.len, rate, xferlen));
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT,
+ ural_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ m_freem(m0);
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static void
+ural_start(struct ifnet *ifp)
+{
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= RAL_TX_LIST_COUNT-1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (ural_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc);
+ }
+}
+
+static void
+ural_watchdog(void *arg)
+{
+ struct ural_softc *sc = (struct ural_softc *)arg;
+
+ RAL_LOCK(sc);
+
+ if (sc->sc_tx_timer > 0) {
+ if (--sc->sc_tx_timer == 0) {
+ device_printf(sc->sc_dev, "device timeout\n");
+ /*ural_init(sc); XXX needs a process context! */
+ sc->sc_ifp->if_oerrors++;
+ RAL_UNLOCK(sc);
+ return;
+ }
+ callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc);
+ }
+
+ RAL_UNLOCK(sc);
+}
+
+static int
+ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ RAL_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ ural_init_locked(sc);
+ startall = 1;
+ } else
+ ural_update_promisc(sc);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ural_stop(sc);
+ }
+ RAL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+ return error;
+}
+
+static void
+ural_set_testmode(struct ural_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_VENDOR_REQUEST;
+ USETW(req.wValue, 4);
+ USETW(req.wIndex, 1);
+ USETW(req.wLength, 0);
+
+ error = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not set test mode: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_EEPROM;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static uint16_t
+ural_read(struct ural_softc *sc, uint16_t reg)
+{
+ usb_device_request_t req;
+ usbd_status error;
+ uint16_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, sizeof (uint16_t));
+
+ error = usbd_do_request(sc->sc_udev, &req, &val);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
+ return 0;
+ }
+
+ return le16toh(val);
+}
+
+static void
+ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_WRITE_MAC;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ error = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_WRITE_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint16_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 5; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
+ break;
+ }
+ if (ntries == 5) {
+ device_printf(sc->sc_dev, "could not write to BBP\n");
+ return;
+ }
+
+ tmp = reg << 8 | val;
+ ural_write(sc, RAL_PHY_CSR7, tmp);
+}
+
+static uint8_t
+ural_bbp_read(struct ural_softc *sc, uint8_t reg)
+{
+ uint16_t val;
+ int ntries;
+
+ val = RAL_BBP_WRITE | reg << 8;
+ ural_write(sc, RAL_PHY_CSR7, val);
+
+ for (ntries = 0; ntries < 5; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
+ break;
+ }
+ if (ntries == 5) {
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+ }
+
+ return ural_read(sc, RAL_PHY_CSR7) & 0xff;
+}
+
+static void
+ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 5; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY))
+ break;
+ }
+ if (ntries == 5) {
+ device_printf(sc->sc_dev, "could not write to RF\n");
+ return;
+ }
+
+ tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3);
+ ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff);
+ ural_write(sc, RAL_PHY_CSR10, tmp >> 16);
+
+ /* remember last written value in sc */
+ sc->rf_regs[reg] = val;
+
+ DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff));
+}
+
+/* ARGUSED */
+static struct ieee80211_node *
+ural_node_alloc(struct ieee80211vap *vap __unused,
+ const uint8_t mac[IEEE80211_ADDR_LEN] __unused)
+{
+ struct ural_node *un;
+
+ un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return un != NULL ? &un->ni : NULL;
+}
+
+static void
+ural_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni);
+}
+
+static void
+ural_scan_start(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = URAL_SCAN_START;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+}
+
+static void
+ural_scan_end(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = URAL_SCAN_END;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+}
+
+static void
+ural_set_channel(struct ieee80211com *ic)
+{
+
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = URAL_SET_CHANNEL;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+}
+
+static void
+ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint8_t power, tmp;
+ u_int i, chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return;
+
+ if (IEEE80211_IS_CHAN_2GHZ(c))
+ power = min(sc->txpow[chan - 1], 31);
+ else
+ power = 31;
+
+ /* adjust txpower using ifconfig settings */
+ power -= (100 - ic->ic_txpowlimit) / 8;
+
+ DPRINTFN(2, ("setting channel to %u, txpower to %u\n", chan, power));
+
+ switch (sc->rf_rev) {
+ case RAL_RF_2522:
+ ural_rf_write(sc, RAL_RF1, 0x00814);
+ ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ break;
+
+ case RAL_RF_2523:
+ ural_rf_write(sc, RAL_RF1, 0x08804);
+ ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2524:
+ ural_rf_write(sc, RAL_RF1, 0x0c808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2525:
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2525E:
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282);
+ break;
+
+ case RAL_RF_2526:
+ ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+ ural_rf_write(sc, RAL_RF1, 0x08804);
+
+ ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+ break;
+
+ /* dual-band RF */
+ case RAL_RF_5222:
+ for (i = 0; ural_rf5222[i].chan != chan; i++);
+
+ ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1);
+ ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4);
+ break;
+ }
+
+ if (ic->ic_opmode != IEEE80211_M_MONITOR &&
+ (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ /* set Japan filter bit for channel 14 */
+ tmp = ural_bbp_read(sc, 70);
+
+ tmp &= ~RAL_JAPAN_FILTER;
+ if (chan == 14)
+ tmp |= RAL_JAPAN_FILTER;
+
+ ural_bbp_write(sc, 70, tmp);
+
+ /* clear CRC errors */
+ ural_read(sc, RAL_STA_CSR0);
+
+ DELAY(10000);
+ ural_disable_rf_tune(sc);
+ }
+
+ /* XXX doesn't belong here */
+ /* update basic rate set */
+ ural_set_basicrates(sc, c);
+}
+
+/*
+ * Disable RF auto-tuning.
+ */
+static void
+ural_disable_rf_tune(struct ural_softc *sc)
+{
+ uint32_t tmp;
+
+ if (sc->rf_rev != RAL_RF_2523) {
+ tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE;
+ ural_rf_write(sc, RAL_RF1, tmp);
+ }
+
+ tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE;
+ ural_rf_write(sc, RAL_RF3, tmp);
+
+ DPRINTFN(2, ("disabling RF autotune\n"));
+}
+
+/*
+ * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF
+ * synchronization.
+ */
+static void
+ural_enable_tsf_sync(struct ural_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint16_t logcwmin, preload, tmp;
+
+ /* first, disable TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+
+ tmp = (16 * vap->iv_bss->ni_intval) << 4;
+ ural_write(sc, RAL_TXRX_CSR18, tmp);
+
+ logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0;
+ preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6;
+ tmp = logcwmin << 12 | preload;
+ ural_write(sc, RAL_TXRX_CSR20, tmp);
+
+ /* finally, enable TSF synchronization */
+ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN;
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ tmp |= RAL_ENABLE_TSF_SYNC(1);
+ else
+ tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR;
+ ural_write(sc, RAL_TXRX_CSR19, tmp);
+
+ DPRINTF(("enabling TSF synchronization\n"));
+}
+
+static void
+ural_update_slot(struct ifnet *ifp)
+{
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t slottime, sifs, eifs;
+
+ slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
+
+ /*
+ * These settings may sound a bit inconsistent but this is what the
+ * reference driver does.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11B) {
+ sifs = 16 - RAL_RXTX_TURNAROUND;
+ eifs = 364;
+ } else {
+ sifs = 10 - RAL_RXTX_TURNAROUND;
+ eifs = 64;
+ }
+
+ ural_write(sc, RAL_MAC_CSR10, slottime);
+ ural_write(sc, RAL_MAC_CSR11, sifs);
+ ural_write(sc, RAL_MAC_CSR12, eifs);
+}
+
+static void
+ural_set_txpreamble(struct ural_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t tmp;
+
+ tmp = ural_read(sc, RAL_TXRX_CSR10);
+
+ tmp &= ~RAL_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ tmp |= RAL_SHORT_PREAMBLE;
+
+ ural_write(sc, RAL_TXRX_CSR10, tmp);
+}
+
+static void
+ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c)
+{
+ /* XXX wrong, take from rate set */
+ /* update basic rate set */
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ /* 11a basic rates: 6, 12, 24Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x150);
+ } else if (IEEE80211_IS_CHAN_ANYG(c)) {
+ /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+ } else {
+ /* 11b basic rates: 1, 2Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x3);
+ }
+}
+
+static void
+ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid)
+{
+ uint16_t tmp;
+
+ tmp = bssid[0] | bssid[1] << 8;
+ ural_write(sc, RAL_MAC_CSR5, tmp);
+
+ tmp = bssid[2] | bssid[3] << 8;
+ ural_write(sc, RAL_MAC_CSR6, tmp);
+
+ tmp = bssid[4] | bssid[5] << 8;
+ ural_write(sc, RAL_MAC_CSR7, tmp);
+
+ DPRINTF(("setting BSSID to %6D\n", bssid, ":"));
+}
+
+static void
+ural_set_macaddr(struct ural_softc *sc, uint8_t *addr)
+{
+ uint16_t tmp;
+
+ tmp = addr[0] | addr[1] << 8;
+ ural_write(sc, RAL_MAC_CSR2, tmp);
+
+ tmp = addr[2] | addr[3] << 8;
+ ural_write(sc, RAL_MAC_CSR3, tmp);
+
+ tmp = addr[4] | addr[5] << 8;
+ ural_write(sc, RAL_MAC_CSR4, tmp);
+
+ DPRINTF(("setting MAC address to %6D\n", addr, ":"));
+}
+
+static void
+ural_update_promisc(struct ural_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ uint32_t tmp;
+
+ tmp = ural_read(sc, RAL_TXRX_CSR2);
+
+ tmp &= ~RAL_DROP_NOT_TO_ME;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RAL_DROP_NOT_TO_ME;
+
+ ural_write(sc, RAL_TXRX_CSR2, tmp);
+
+ DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving"));
+}
+
+static const char *
+ural_get_rf(int rev)
+{
+ switch (rev) {
+ case RAL_RF_2522: return "RT2522";
+ case RAL_RF_2523: return "RT2523";
+ case RAL_RF_2524: return "RT2524";
+ case RAL_RF_2525: return "RT2525";
+ case RAL_RF_2525E: return "RT2525e";
+ case RAL_RF_2526: return "RT2526";
+ case RAL_RF_5222: return "RT5222";
+ default: return "unknown";
+ }
+}
+
+static void
+ural_read_eeprom(struct ural_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t val;
+
+ ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2);
+ val = le16toh(val);
+ sc->rf_rev = (val >> 11) & 0x7;
+ sc->hw_radio = (val >> 10) & 0x1;
+ sc->led_mode = (val >> 6) & 0x7;
+ sc->rx_ant = (val >> 4) & 0x3;
+ sc->tx_ant = (val >> 2) & 0x3;
+ sc->nb_ant = val & 0x3;
+
+ /* read MAC address */
+ ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_myaddr, 6);
+
+ /* read default values for BBP registers */
+ ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16);
+
+ /* read Tx power for all b/g channels */
+ ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14);
+}
+
+static int
+ural_bbp_init(struct ural_softc *sc)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ int i, ntries;
+
+ /* wait for BBP to be ready */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0)
+ break;
+ DELAY(1000);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+ return EIO;
+ }
+
+ /* initialize BBP registers to default values */
+ for (i = 0; i < N(ural_def_bbp); i++)
+ ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val);
+
+#if 0
+ /* initialize BBP registers to values stored in EEPROM */
+ for (i = 0; i < 16; i++) {
+ if (sc->bbp_prom[i].reg == 0xff)
+ continue;
+ ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+ }
+#endif
+
+ return 0;
+#undef N
+}
+
+static void
+ural_set_txantenna(struct ural_softc *sc, int antenna)
+{
+ uint16_t tmp;
+ uint8_t tx;
+
+ tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK;
+ if (antenna == 1)
+ tx |= RAL_BBP_ANTA;
+ else if (antenna == 2)
+ tx |= RAL_BBP_ANTB;
+ else
+ tx |= RAL_BBP_DIVERSITY;
+
+ /* need to force I/Q flip for RF 2525e, 2526 and 5222 */
+ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 ||
+ sc->rf_rev == RAL_RF_5222)
+ tx |= RAL_BBP_FLIPIQ;
+
+ ural_bbp_write(sc, RAL_BBP_TX, tx);
+
+ /* update values in PHY_CSR5 and PHY_CSR6 */
+ tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7;
+ ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7));
+
+ tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7;
+ ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7));
+}
+
+static void
+ural_set_rxantenna(struct ural_softc *sc, int antenna)
+{
+ uint8_t rx;
+
+ rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK;
+ if (antenna == 1)
+ rx |= RAL_BBP_ANTA;
+ else if (antenna == 2)
+ rx |= RAL_BBP_ANTB;
+ else
+ rx |= RAL_BBP_DIVERSITY;
+
+ /* need to force no I/Q flip for RF 2525e and 2526 */
+ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526)
+ rx &= ~RAL_BBP_FLIPIQ;
+
+ ural_bbp_write(sc, RAL_BBP_RX, rx);
+}
+
+static void
+ural_init_locked(struct ural_softc *sc)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ural_rx_data *data;
+ uint16_t tmp;
+ usbd_status error;
+ int i, ntries;
+
+ ural_set_testmode(sc);
+ ural_write(sc, 0x308, 0x00f0); /* XXX magic */
+
+ ural_stop(sc);
+
+ /* initialize MAC registers to default values */
+ for (i = 0; i < N(ural_def_mac); i++)
+ ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val);
+
+ /* wait for BBP and RF to wake up (this can take a long time!) */
+ for (ntries = 0; ntries < 100; ntries++) {
+ tmp = ural_read(sc, RAL_MAC_CSR17);
+ if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) ==
+ (RAL_BBP_AWAKE | RAL_RF_AWAKE))
+ break;
+ DELAY(1000);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
+ goto fail;
+ }
+
+ /* we're ready! */
+ ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY);
+
+ /* set basic rate set (will be updated later) */
+ ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+
+ if (ural_bbp_init(sc) != 0)
+ goto fail;
+
+ ural_set_chan(sc, ic->ic_curchan);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ ural_set_txantenna(sc, sc->tx_ant);
+ ural_set_rxantenna(sc, sc->rx_ant);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ ural_set_macaddr(sc, ic->ic_myaddr);
+
+ /*
+ * Allocate xfer for AMRR statistics requests.
+ */
+ sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->amrr_xfer == NULL) {
+ device_printf(sc->sc_dev, "could not allocate AMRR xfer\n");
+ goto fail;
+ }
+
+ /*
+ * Open Tx and Rx USB bulk pipes.
+ */
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_tx_pipeh);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not open Tx pipe: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_rx_pipeh);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not open Rx pipe: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ error = ural_alloc_tx_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Tx list\n");
+ goto fail;
+ }
+
+ error = ural_alloc_rx_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Rx list\n");
+ goto fail;
+ }
+
+ /*
+ * Start up the receive pipe.
+ */
+ for (i = 0; i < RAL_RX_LIST_COUNT; i++) {
+ data = &sc->rx_data[i];
+
+ usbd_setup_xfer(data->xfer, sc->sc_rx_pipeh, data, data->buf,
+ MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ural_rxeof);
+ usbd_transfer(data->xfer);
+ }
+
+ /* kick Rx */
+ tmp = RAL_DROP_PHY | RAL_DROP_CRC;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ tmp |= RAL_DROP_TODS;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RAL_DROP_NOT_TO_ME;
+ }
+ ural_write(sc, RAL_TXRX_CSR2, tmp);
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ return;
+
+fail: ural_stop(sc);
+#undef N
+}
+
+static void
+ural_init(void *priv)
+{
+ struct ural_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RAL_LOCK(sc);
+ ural_init_locked(sc);
+ RAL_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+ural_stop(void *priv)
+{
+ struct ural_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ sc->sc_tx_timer = 0;
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ /* disable Rx */
+ ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX);
+
+ /* reset ASIC and BBP (but won't reset MAC registers!) */
+ ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP);
+ ural_write(sc, RAL_MAC_CSR1, 0);
+
+ if (sc->amrr_xfer != NULL) {
+ usbd_free_xfer(sc->amrr_xfer);
+ sc->amrr_xfer = NULL;
+ }
+
+ if (sc->sc_rx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_rx_pipeh);
+ usbd_close_pipe(sc->sc_rx_pipeh);
+ sc->sc_rx_pipeh = NULL;
+ }
+
+ if (sc->sc_tx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ sc->sc_tx_pipeh = NULL;
+ }
+
+ ural_free_rx_list(sc);
+ ural_free_tx_list(sc);
+}
+
+static int
+ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ural_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->tx_queued >= RAL_TX_LIST_COUNT) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return EIO;
+ }
+
+ ifp->if_opackets++;
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ if (ural_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ if (ural_tx_raw(sc, m, ni, params) != 0)
+ goto bad;
+ }
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc);
+
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ return EIO; /* XXX */
+}
+
+static void
+ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ural_vap *uvp = URAL_VAP(vap);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni);
+
+ callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap);
+}
+
+static void
+ural_amrr_timeout(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct ural_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ usb_device_request_t req;
+
+ /*
+ * Asynchronously read statistic registers (cleared by read).
+ */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, RAL_STA_CSR0);
+ USETW(req.wLength, sizeof sc->sta);
+
+ usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap,
+ USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0,
+ ural_amrr_update);
+ (void)usbd_transfer(sc->amrr_xfer);
+}
+
+static void
+ural_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct ieee80211vap *vap = priv;
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni = vap->iv_bss;
+ int ok, fail;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ device_printf(sc->sc_dev, "could not retrieve Tx statistics - "
+ "cancelling automatic rate control\n");
+ return;
+ }
+
+ ok = sc->sta[7] + /* TX ok w/o retry */
+ sc->sta[8]; /* TX ok w/ retry */
+ fail = sc->sta[9]; /* TX retry-fail count */
+
+ ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn,
+ ok+fail, ok, sc->sta[8] + fail);
+ (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn);
+
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
+
+ callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap);
+}
diff --git a/sys/legacy/dev/usb/if_uralreg.h b/sys/legacy/dev/usb/if_uralreg.h
new file mode 100644
index 0000000..428089f
--- /dev/null
+++ b/sys/legacy/dev/usb/if_uralreg.h
@@ -0,0 +1,210 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RAL_NOISE_FLOOR -95
+#define RAL_RSSI_CORR 120
+
+#define RAL_RX_DESC_SIZE (sizeof (struct ural_rx_desc))
+#define RAL_TX_DESC_SIZE (sizeof (struct ural_tx_desc))
+
+#define RAL_CONFIG_NO 1
+#define RAL_IFACE_INDEX 0
+
+#define RAL_VENDOR_REQUEST 0x01
+#define RAL_WRITE_MAC 0x02
+#define RAL_READ_MAC 0x03
+#define RAL_WRITE_MULTI_MAC 0x06
+#define RAL_READ_MULTI_MAC 0x07
+#define RAL_READ_EEPROM 0x09
+
+/*
+ * MAC registers.
+ */
+#define RAL_MAC_CSR0 0x0400 /* ASIC Version */
+#define RAL_MAC_CSR1 0x0402 /* System control */
+#define RAL_MAC_CSR2 0x0404 /* MAC addr0 */
+#define RAL_MAC_CSR3 0x0406 /* MAC addr1 */
+#define RAL_MAC_CSR4 0x0408 /* MAC addr2 */
+#define RAL_MAC_CSR5 0x040a /* BSSID0 */
+#define RAL_MAC_CSR6 0x040c /* BSSID1 */
+#define RAL_MAC_CSR7 0x040e /* BSSID2 */
+#define RAL_MAC_CSR8 0x0410 /* Max frame length */
+#define RAL_MAC_CSR9 0x0412 /* Timer control */
+#define RAL_MAC_CSR10 0x0414 /* Slot time */
+#define RAL_MAC_CSR11 0x0416 /* IFS */
+#define RAL_MAC_CSR12 0x0418 /* EIFS */
+#define RAL_MAC_CSR13 0x041a /* Power mode0 */
+#define RAL_MAC_CSR14 0x041c /* Power mode1 */
+#define RAL_MAC_CSR15 0x041e /* Power saving transition0 */
+#define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */
+#define RAL_MAC_CSR17 0x0422 /* Power state control */
+#define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */
+#define RAL_MAC_CSR19 0x0426 /* GPIO control */
+#define RAL_MAC_CSR20 0x0428 /* LED control0 */
+#define RAL_MAC_CSR22 0x042c /* XXX not documented */
+
+/*
+ * Tx/Rx Registers.
+ */
+#define RAL_TXRX_CSR0 0x0440 /* Security control */
+#define RAL_TXRX_CSR2 0x0444 /* Rx control */
+#define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */
+#define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */
+#define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */
+#define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */
+#define RAL_TXRX_CSR10 0x0454 /* Auto responder control */
+#define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */
+#define RAL_TXRX_CSR18 0x0464 /* Beacon interval */
+#define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */
+#define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */
+#define RAL_TXRX_CSR21 0x046a /* XXX not documented */
+
+/*
+ * Security registers.
+ */
+#define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */
+
+/*
+ * PHY registers.
+ */
+#define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */
+#define RAL_PHY_CSR4 0x04c8 /* Interface configuration */
+#define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */
+#define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */
+#define RAL_PHY_CSR7 0x04ce /* BBP serial control */
+#define RAL_PHY_CSR8 0x04d0 /* BBP serial status */
+#define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */
+#define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */
+
+/*
+ * Statistics registers.
+ */
+#define RAL_STA_CSR0 0x04e0 /* FCS error */
+
+
+#define RAL_DISABLE_RX (1 << 0)
+#define RAL_DROP_CRC (1 << 1)
+#define RAL_DROP_PHY (1 << 2)
+#define RAL_DROP_CTL (1 << 3)
+#define RAL_DROP_NOT_TO_ME (1 << 4)
+#define RAL_DROP_TODS (1 << 5)
+#define RAL_DROP_BAD_VERSION (1 << 6)
+#define RAL_DROP_MULTICAST (1 << 9)
+#define RAL_DROP_BROADCAST (1 << 10)
+
+#define RAL_SHORT_PREAMBLE (1 << 2)
+
+#define RAL_RESET_ASIC (1 << 0)
+#define RAL_RESET_BBP (1 << 1)
+#define RAL_HOST_READY (1 << 2)
+
+#define RAL_ENABLE_TSF (1 << 0)
+#define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1)
+#define RAL_ENABLE_TBCN (1 << 3)
+#define RAL_ENABLE_BEACON_GENERATOR (1 << 4)
+
+#define RAL_RF_AWAKE (3 << 7)
+#define RAL_BBP_AWAKE (3 << 5)
+
+#define RAL_BBP_WRITE (1 << 15)
+#define RAL_BBP_BUSY (1 << 0)
+
+#define RAL_RF1_AUTOTUNE 0x08000
+#define RAL_RF3_AUTOTUNE 0x00040
+
+#define RAL_RF_2522 0x00
+#define RAL_RF_2523 0x01
+#define RAL_RF_2524 0x02
+#define RAL_RF_2525 0x03
+#define RAL_RF_2525E 0x04
+#define RAL_RF_2526 0x05
+/* dual-band RF */
+#define RAL_RF_5222 0x10
+
+#define RAL_BBP_VERSION 0
+#define RAL_BBP_TX 2
+#define RAL_BBP_RX 14
+
+#define RAL_BBP_ANTA 0x00
+#define RAL_BBP_DIVERSITY 0x01
+#define RAL_BBP_ANTB 0x02
+#define RAL_BBP_ANTMASK 0x03
+#define RAL_BBP_FLIPIQ 0x04
+
+#define RAL_JAPAN_FILTER 0x08
+
+struct ural_tx_desc {
+ uint32_t flags;
+#define RAL_TX_RETRY(x) ((x) << 4)
+#define RAL_TX_MORE_FRAG (1 << 8)
+#define RAL_TX_ACK (1 << 9)
+#define RAL_TX_TIMESTAMP (1 << 10)
+#define RAL_TX_OFDM (1 << 11)
+#define RAL_TX_NEWSEQ (1 << 12)
+
+#define RAL_TX_IFS_MASK 0x00006000
+#define RAL_TX_IFS_BACKOFF (0 << 13)
+#define RAL_TX_IFS_SIFS (1 << 13)
+#define RAL_TX_IFS_NEWBACKOFF (2 << 13)
+#define RAL_TX_IFS_NONE (3 << 13)
+
+ uint16_t wme;
+#define RAL_LOGCWMAX(x) (((x) & 0xf) << 12)
+#define RAL_LOGCWMIN(x) (((x) & 0xf) << 8)
+#define RAL_AIFSN(x) (((x) & 0x3) << 6)
+#define RAL_IVOFFSET(x) (((x) & 0x3f))
+
+ uint16_t reserved1;
+ uint8_t plcp_signal;
+ uint8_t plcp_service;
+#define RAL_PLCP_LENGEXT 0x80
+
+ uint8_t plcp_length_lo;
+ uint8_t plcp_length_hi;
+ uint32_t iv;
+ uint32_t eiv;
+} __packed;
+
+struct ural_rx_desc {
+ uint32_t flags;
+#define RAL_RX_CRC_ERROR (1 << 5)
+#define RAL_RX_OFDM (1 << 6)
+#define RAL_RX_PHY_ERROR (1 << 7)
+
+ uint8_t rssi;
+ uint8_t rate;
+ uint16_t reserved;
+
+ uint32_t iv;
+ uint32_t eiv;
+} __packed;
+
+#define RAL_RF_LOBUSY (1 << 15)
+#define RAL_RF_BUSY (1 << 31)
+#define RAL_RF_20BIT (20 << 24)
+
+#define RAL_RF1 0
+#define RAL_RF2 2
+#define RAL_RF3 1
+#define RAL_RF4 3
+
+#define RAL_EEPROM_ADDRESS 0x0004
+#define RAL_EEPROM_TXPOWER 0x003c
+#define RAL_EEPROM_CONFIG0 0x0016
+#define RAL_EEPROM_BBP_BASE 0x001c
diff --git a/sys/legacy/dev/usb/if_uralvar.h b/sys/legacy/dev/usb/if_uralvar.h
new file mode 100644
index 0000000..39aef9e
--- /dev/null
+++ b/sys/legacy/dev/usb/if_uralvar.h
@@ -0,0 +1,157 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RAL_RX_LIST_COUNT 1
+#define RAL_TX_LIST_COUNT 8
+
+#define URAL_SCAN_START 1
+#define URAL_SCAN_END 2
+#define URAL_SET_CHANNEL 3
+
+
+struct ural_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ uint8_t wr_antenna;
+ uint8_t wr_antsignal;
+};
+
+#define RAL_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct ural_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_antenna;
+};
+
+#define RAL_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct ural_softc;
+
+struct ural_tx_data {
+ struct ural_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+};
+
+struct ural_rx_data {
+ struct ural_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct mbuf *m;
+};
+
+struct ural_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define URAL_NODE(ni) ((struct ural_node *)(ni))
+
+struct ural_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct callout amrr_ch;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define URAL_VAP(vap) ((struct ural_vap *)(vap))
+
+struct ural_softc {
+ struct ifnet *sc_ifp;
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+
+ const struct ieee80211_rate_table *sc_rates;
+
+ int sc_rx_no;
+ int sc_tx_no;
+
+ uint32_t asic_rev;
+ uint8_t rf_rev;
+
+ usbd_xfer_handle amrr_xfer;
+
+ usbd_pipe_handle sc_rx_pipeh;
+ usbd_pipe_handle sc_tx_pipeh;
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int sc_scan_action; /* should be an enum */
+ struct usb_task sc_task;
+ struct usb_task sc_scantask;
+
+ struct ural_rx_data rx_data[RAL_RX_LIST_COUNT];
+ struct ural_tx_data tx_data[RAL_TX_LIST_COUNT];
+ int tx_queued;
+ int tx_cur;
+
+ struct mtx sc_mtx;
+
+ struct callout watchdog_ch;
+ int sc_tx_timer;
+
+ uint16_t sta[11];
+ uint32_t rf_regs[4];
+ uint8_t txpow[14];
+
+ struct {
+ uint8_t val;
+ uint8_t reg;
+ } __packed bbp_prom[16];
+
+ int led_mode;
+ int hw_radio;
+ int rx_ant;
+ int tx_ant;
+ int nb_ant;
+
+ struct ural_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+
+ struct ural_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#if 0
+#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#else
+#define RAL_LOCK(sc) do { ((sc) = (sc)); mtx_lock(&Giant); } while (0)
+#define RAL_UNLOCK(sc) mtx_unlock(&Giant)
+#endif
diff --git a/sys/legacy/dev/usb/if_urtw.c b/sys/legacy/dev/usb/if_urtw.c
new file mode 100644
index 0000000..d1a3a84
--- /dev/null
+++ b/sys/legacy/dev/usb/if_urtw.c
@@ -0,0 +1,3324 @@
+/*-
+ * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/if_urtwreg.h>
+#include <dev/usb/if_urtwvar.h>
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW, 0, "USB Realtek 8187L");
+#ifdef URTW_DEBUG
+int urtw_debug = 0;
+SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RW, &urtw_debug, 0,
+ "control debugging printfs");
+TUNABLE_INT("hw.usb.urtw.debug", &urtw_debug);
+enum {
+ URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */
+ URTW_DEBUG_RESET = 0x00000004, /* reset processing */
+ URTW_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */
+ URTW_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */
+ URTW_DEBUG_STATE = 0x00000020, /* 802.11 state transitions */
+ URTW_DEBUG_STAT = 0x00000040, /* statistic */
+ URTW_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (sc->sc_debug & (m)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG;
+SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RW,
+ &urtw_preamble_mode, 0, "set the preable mode (long or short)");
+TUNABLE_INT("hw.usb.urtw.preamble_mode", &urtw_preamble_mode);
+
+/* recognized device vendors/products */
+static const struct usb_devno urtw_devs[] = {
+#define URTW_DEV(v,p) { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }
+ URTW_DEV(REALTEK, RTL8187),
+ URTW_DEV(NETGEAR, WG111V2)
+#undef URTW_DEV
+};
+
+#define urtw_read8_m(sc, val, data) do { \
+ error = urtw_read8_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_write8_m(sc, val, data) do { \
+ error = urtw_write8_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_read16_m(sc, val, data) do { \
+ error = urtw_read16_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_write16_m(sc, val, data) do { \
+ error = urtw_write16_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_read32_m(sc, val, data) do { \
+ error = urtw_read32_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_write32_m(sc, val, data) do { \
+ error = urtw_write32_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_8187_write_phy_ofdm(sc, val, data) do { \
+ error = urtw_8187_write_phy_ofdm_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_8187_write_phy_cck(sc, val, data) do { \
+ error = urtw_8187_write_phy_cck_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_8225_write(sc, val, data) do { \
+ error = urtw_8225_write_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+
+struct urtw_pair {
+ uint32_t reg;
+ uint32_t val;
+};
+
+static uint8_t urtw_8225_agc[] = {
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b,
+ 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90,
+ 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85,
+ 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a,
+ 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f,
+ 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24,
+ 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19,
+ 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
+ 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+static uint32_t urtw_8225_channel[] = {
+ 0x0000, /* dummy channel 0 */
+ 0x085c, /* 1 */
+ 0x08dc, /* 2 */
+ 0x095c, /* 3 */
+ 0x09dc, /* 4 */
+ 0x0a5c, /* 5 */
+ 0x0adc, /* 6 */
+ 0x0b5c, /* 7 */
+ 0x0bdc, /* 8 */
+ 0x0c5c, /* 9 */
+ 0x0cdc, /* 10 */
+ 0x0d5c, /* 11 */
+ 0x0ddc, /* 12 */
+ 0x0e5c, /* 13 */
+ 0x0f72, /* 14 */
+};
+
+static uint8_t urtw_8225_gain[] = {
+ 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */
+ 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */
+ 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */
+ 0x33, 0x80, 0x79, 0xc5, /* -78dbm */
+ 0x43, 0x78, 0x76, 0xc5, /* -74dbm */
+ 0x53, 0x60, 0x73, 0xc5, /* -70dbm */
+ 0x63, 0x58, 0x70, 0xc5, /* -66dbm */
+};
+
+static struct urtw_pair urtw_8225_rf_part1[] = {
+ { 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
+ { 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a },
+ { 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 },
+ { 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 },
+};
+
+static struct urtw_pair urtw_8225_rf_part2[] = {
+ { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 },
+ { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 },
+ { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 },
+ { 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 },
+ { 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 },
+ { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef },
+ { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 },
+ { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 },
+ { 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 },
+ { 0x27, 0x88 }
+};
+
+static struct urtw_pair urtw_8225_rf_part3[] = {
+ { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 },
+ { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b },
+ { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 },
+ { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d },
+ { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e },
+ { 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a },
+ { 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 }
+};
+
+static uint16_t urtw_8225_rxgain[] = {
+ 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
+ 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
+ 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
+ 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
+ 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
+ 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
+ 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
+ 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
+ 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
+ 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
+ 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
+ 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
+};
+
+static uint8_t urtw_8225_threshold[] = {
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd,
+};
+
+static uint8_t urtw_8225_tx_gain_cck_ofdm[] = {
+ 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
+};
+
+static uint8_t urtw_8225_txpwr_cck[] = {
+ 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
+ 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
+ 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
+ 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
+};
+
+static uint8_t urtw_8225_txpwr_cck_ch14[] = {
+ 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
+ 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static uint8_t urtw_8225_txpwr_ofdm[]={
+ 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
+};
+
+static uint8_t urtw_8225v2_gain_bg[]={
+ 0x23, 0x15, 0xa5, /* -82-1dbm */
+ 0x23, 0x15, 0xb5, /* -82-2dbm */
+ 0x23, 0x15, 0xc5, /* -82-3dbm */
+ 0x33, 0x15, 0xc5, /* -78dbm */
+ 0x43, 0x15, 0xc5, /* -74dbm */
+ 0x53, 0x15, 0xc5, /* -70dbm */
+ 0x63, 0x15, 0xc5, /* -66dbm */
+};
+
+static struct urtw_pair urtw_8225v2_rf_part1[] = {
+ { 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
+ { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a },
+ { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb },
+ { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 }
+};
+
+static struct urtw_pair urtw_8225v2_rf_part2[] = {
+ { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 },
+ { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 },
+ { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x08 }, { 0x0b, 0x80 },
+ { 0x0c, 0x01 }, { 0x0d, 0x43 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 },
+ { 0x10, 0x84 }, { 0x11, 0x07 }, { 0x12, 0x20 }, { 0x13, 0x20 },
+ { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 },
+ { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x15 },
+ { 0x1c, 0x04 }, { 0x1d, 0xc5 }, { 0x1e, 0x95 }, { 0x1f, 0x75 },
+ { 0x20, 0x1f }, { 0x21, 0x17 }, { 0x22, 0x16 }, { 0x23, 0x80 },
+ { 0x24, 0x46 }, { 0x25, 0x00 }, { 0x26, 0x90 }, { 0x27, 0x88 }
+};
+
+static struct urtw_pair urtw_8225v2_rf_part3[] = {
+ { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 },
+ { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x09, 0x11 },
+ { 0x0a, 0x17 }, { 0x0b, 0x11 }, { 0x10, 0x9b }, { 0x11, 0x88 },
+ { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 },
+ { 0x1b, 0x08 }, { 0x1d, 0x00 }, { 0x40, 0x86 }, { 0x41, 0x9d },
+ { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x36 }, { 0x45, 0x35 },
+ { 0x46, 0x2e }, { 0x47, 0x25 }, { 0x48, 0x1c }, { 0x49, 0x12 },
+ { 0x4a, 0x09 }, { 0x4b, 0x04 }, { 0x4c, 0x05 }
+};
+
+static uint16_t urtw_8225v2_rxgain[] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009,
+ 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141,
+ 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183,
+ 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244,
+ 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288,
+ 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345,
+ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389,
+ 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393,
+ 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d,
+ 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9,
+ 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
+ 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
+};
+
+static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+};
+
+static uint8_t urtw_8225v2_txpwr_cck[] = {
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
+};
+
+static uint8_t urtw_8225v2_txpwr_cck_ch14[] = {
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
+};
+
+static struct urtw_pair urtw_ratetable[] = {
+ { 2, 0 }, { 4, 1 }, { 11, 2 }, { 12, 4 }, { 18, 5 },
+ { 22, 3 }, { 24, 6 }, { 36, 7 }, { 48, 8 }, { 72, 9 },
+ { 96, 10 }, { 108, 11 }
+};
+
+static struct ieee80211vap *urtw_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void urtw_vap_delete(struct ieee80211vap *);
+static void urtw_init(void *);
+static void urtw_stop(struct ifnet *, int);
+static int urtw_ioctl(struct ifnet *, u_long, caddr_t);
+static void urtw_start(struct ifnet *);
+static int urtw_alloc_rx_data_list(struct urtw_softc *);
+static int urtw_alloc_tx_data_list(struct urtw_softc *);
+static void urtw_free_data_list(struct urtw_softc *,
+ usbd_pipe_handle, usbd_pipe_handle,
+ struct urtw_data data[], int);
+static int urtw_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void urtw_scan_start(struct ieee80211com *);
+static void urtw_scan_end(struct ieee80211com *);
+static void urtw_set_channel(struct ieee80211com *);
+static void urtw_update_mcast(struct ifnet *);
+static void urtw_rxeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+static int urtw_tx_start(struct urtw_softc *,
+ struct ieee80211_node *, struct mbuf *, int);
+static void urtw_txeof_low(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+static void urtw_txeof_normal(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+static int urtw_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static void urtw_ledtask(void *);
+static void urtw_ledusbtask(void *);
+static void urtw_ctxtask(void *);
+static void urtw_task(void *);
+static void urtw_watchdog(void *);
+static void urtw_set_multi(void *);
+static int urtw_isbmode(uint16_t);
+static uint16_t urtw_rate2rtl(int);
+static uint16_t urtw_rtl2rate(int);
+static usbd_status urtw_set_rate(struct urtw_softc *);
+static usbd_status urtw_update_msr(struct urtw_softc *);
+static usbd_status urtw_read8_c(struct urtw_softc *, int, uint8_t *);
+static usbd_status urtw_read16_c(struct urtw_softc *, int, uint16_t *);
+static usbd_status urtw_read32_c(struct urtw_softc *, int, uint32_t *);
+static usbd_status urtw_write8_c(struct urtw_softc *, int, uint8_t);
+static usbd_status urtw_write16_c(struct urtw_softc *, int, uint16_t);
+static usbd_status urtw_write32_c(struct urtw_softc *, int, uint32_t);
+static usbd_status urtw_eprom_cs(struct urtw_softc *, int);
+static usbd_status urtw_eprom_ck(struct urtw_softc *);
+static usbd_status urtw_eprom_sendbits(struct urtw_softc *, int16_t *,
+ int);
+static usbd_status urtw_eprom_read32(struct urtw_softc *, uint32_t,
+ uint32_t *);
+static usbd_status urtw_eprom_readbit(struct urtw_softc *, int16_t *);
+static usbd_status urtw_eprom_writebit(struct urtw_softc *, int16_t);
+static usbd_status urtw_get_macaddr(struct urtw_softc *);
+static usbd_status urtw_get_txpwr(struct urtw_softc *);
+static usbd_status urtw_get_rfchip(struct urtw_softc *);
+static usbd_status urtw_led_init(struct urtw_softc *);
+static usbd_status urtw_8185_rf_pins_enable(struct urtw_softc *);
+static usbd_status urtw_8185_tx_antenna(struct urtw_softc *, uint8_t);
+static usbd_status urtw_8187_write_phy(struct urtw_softc *, uint8_t,
+ uint32_t);
+static usbd_status urtw_8187_write_phy_ofdm_c(struct urtw_softc *,
+ uint8_t, uint32_t);
+static usbd_status urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t,
+ uint32_t);
+static usbd_status urtw_8225_setgain(struct urtw_softc *, int16_t);
+static usbd_status urtw_8225_usb_init(struct urtw_softc *);
+static usbd_status urtw_8225_write_c(struct urtw_softc *, uint8_t,
+ uint16_t);
+static usbd_status urtw_8225_write_s16(struct urtw_softc *, uint8_t, int,
+ uint16_t *);
+static usbd_status urtw_8225_read(struct urtw_softc *, uint8_t,
+ uint32_t *);
+static usbd_status urtw_8225_rf_init(struct urtw_softc *);
+static usbd_status urtw_8225_rf_set_chan(struct urtw_softc *, int);
+static usbd_status urtw_8225_rf_set_sens(struct urtw_softc *, int);
+static usbd_status urtw_8225_set_txpwrlvl(struct urtw_softc *, int);
+static usbd_status urtw_8225v2_rf_init(struct urtw_softc *);
+static usbd_status urtw_8225v2_rf_set_chan(struct urtw_softc *, int);
+static usbd_status urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int);
+static usbd_status urtw_8225v2_setgain(struct urtw_softc *, int16_t);
+static usbd_status urtw_8225_isv2(struct urtw_softc *, int *);
+static usbd_status urtw_read8e(struct urtw_softc *, int, uint8_t *);
+static usbd_status urtw_write8e(struct urtw_softc *, int, uint8_t);
+static usbd_status urtw_8180_set_anaparam(struct urtw_softc *, uint32_t);
+static usbd_status urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t);
+static usbd_status urtw_open_pipes(struct urtw_softc *);
+static usbd_status urtw_close_pipes(struct urtw_softc *);
+static usbd_status urtw_intr_enable(struct urtw_softc *);
+static usbd_status urtw_intr_disable(struct urtw_softc *);
+static usbd_status urtw_reset(struct urtw_softc *);
+static usbd_status urtw_led_on(struct urtw_softc *, int);
+static usbd_status urtw_led_ctl(struct urtw_softc *, int);
+static usbd_status urtw_led_blink(struct urtw_softc *);
+static usbd_status urtw_led_mode0(struct urtw_softc *, int);
+static usbd_status urtw_led_mode1(struct urtw_softc *, int);
+static usbd_status urtw_led_mode2(struct urtw_softc *, int);
+static usbd_status urtw_led_mode3(struct urtw_softc *, int);
+static usbd_status urtw_rx_setconf(struct urtw_softc *);
+static usbd_status urtw_rx_enable(struct urtw_softc *);
+static usbd_status urtw_tx_enable(struct urtw_softc *sc);
+
+static int
+urtw_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ const struct usb_devno *ud;
+
+ if (uaa->iface != NULL)
+ return UMATCH_NONE;
+ ud = usb_lookup(urtw_devs, uaa->vendor, uaa->product);
+
+ return (ud != NULL ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+static int
+urtw_attach(device_t dev)
+{
+ int ret = 0;
+ struct urtw_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct ieee80211com *ic;
+ struct ifnet *ifp;
+ uint8_t bands;
+ uint32_t data;
+ usbd_status error;
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+#ifdef URTW_DEBUG
+ sc->sc_debug = urtw_debug;
+#endif
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+ callout_init(&sc->sc_led_ch, 0);
+ callout_init(&sc->sc_watchdog_ch, 0);
+ usb_init_task(&sc->sc_ledtask, urtw_ledusbtask, sc);
+ usb_init_task(&sc->sc_ctxtask, urtw_ctxtask, sc);
+ usb_init_task(&sc->sc_task, urtw_task, sc);
+
+ urtw_read32_m(sc, URTW_RX, &data);
+ sc->sc_epromtype = (data & URTW_RX_9356SEL) ? URTW_EEPROM_93C56 :
+ URTW_EEPROM_93C46;
+
+ error = urtw_get_rfchip(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_get_macaddr(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_get_txpwr(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_led_init(sc);
+ if (error != 0)
+ goto fail;
+
+ sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY;
+ sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY;
+ sc->sc_currate = 3;
+ sc->sc_preamble_mode = urtw_preamble_mode;
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not allocate ifnet\n");
+ ret = ENXIO;
+ goto fail;
+ }
+
+ ifp->if_softc = sc;
+ if_initname(ifp, "urtw", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT; /* USB stack is still under Giant lock */
+ ifp->if_init = urtw_init;
+ ifp->if_ioctl = urtw_ioctl;
+ ifp->if_start = urtw_start;
+ /* XXX URTW_TX_DATA_LIST_COUNT */
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic = ifp->if_l2com;
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA | /* station mode */
+ IEEE80211_C_MONITOR | /* monitor mode supported */
+ IEEE80211_C_TXPMGT | /* tx power management */
+ IEEE80211_C_SHPREAMBLE | /* short preamble supported */
+ IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_BGSCAN | /* capable of bg scanning */
+ IEEE80211_C_WPA; /* 802.11i */
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid);
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_raw_xmit = urtw_raw_xmit;
+ ic->ic_scan_start = urtw_scan_start;
+ ic->ic_scan_end = urtw_scan_end;
+ ic->ic_set_channel = urtw_set_channel;
+
+ ic->ic_vap_create = urtw_vap_create;
+ ic->ic_vap_delete = urtw_vap_delete;
+ ic->ic_update_mcast = urtw_update_mcast;
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
+
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(URTW_RX_RADIOTAP_PRESENT);
+
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(URTW_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
+fail:
+ return (ret);
+}
+
+static usbd_status
+urtw_open_pipes(struct urtw_softc *sc)
+{
+ usbd_status error;
+
+ /*
+ * NB: there is no way to distinguish each pipes so we need to hardcode
+ * pipe numbers
+ */
+
+ /* tx pipe - low priority packets */
+ error = usbd_open_pipe(sc->sc_iface, 0x2, USBD_EXCLUSIVE_USE,
+ &sc->sc_txpipe_low);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not open Tx low pipe: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+ /* tx pipe - normal priority packets */
+ error = usbd_open_pipe(sc->sc_iface, 0x3, USBD_EXCLUSIVE_USE,
+ &sc->sc_txpipe_normal);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not open Tx normal pipe: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+ /* rx pipe */
+ error = usbd_open_pipe(sc->sc_iface, 0x81, USBD_EXCLUSIVE_USE,
+ &sc->sc_rxpipe);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not open Rx pipe: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ return (0);
+fail:
+ (void)urtw_close_pipes(sc);
+ return (error);
+}
+
+static usbd_status
+urtw_close_pipes(struct urtw_softc *sc)
+{
+ usbd_status error = 0;
+
+ if (sc->sc_rxpipe != NULL) {
+ error = usbd_close_pipe(sc->sc_rxpipe);
+ if (error != 0)
+ goto fail;
+ sc->sc_rxpipe = NULL;
+ }
+ if (sc->sc_txpipe_low != NULL) {
+ error = usbd_close_pipe(sc->sc_txpipe_low);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpipe_low = NULL;
+ }
+ if (sc->sc_txpipe_normal != NULL) {
+ error = usbd_close_pipe(sc->sc_txpipe_normal);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpipe_normal = NULL;
+ }
+fail:
+ return (error);
+}
+
+static int
+urtw_alloc_data_list(struct urtw_softc *sc, struct urtw_data data[],
+ int ndata, int maxsz, int fillmbuf)
+{
+ int i, error;
+
+ for (i = 0; i < ndata; i++) {
+ struct urtw_data *dp = &data[i];
+
+ dp->sc = sc;
+ dp->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (dp->xfer == NULL) {
+ device_printf(sc->sc_dev, "could not allocate xfer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ if (fillmbuf) {
+ dp->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (dp->m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ dp->buf = mtod(dp->m, uint8_t *);
+ } else {
+ dp->m = NULL;
+ dp->buf = usbd_alloc_buffer(dp->xfer, maxsz);
+ if (dp->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ if (((unsigned long)dp->buf) % 4)
+ device_printf(sc->sc_dev,
+ "warn: unaligned buffer %p\n", dp->buf);
+ }
+ dp->ni = NULL;
+ }
+
+ return 0;
+
+fail: urtw_free_data_list(sc, NULL, NULL, data, ndata);
+ return error;
+}
+
+static void
+urtw_free_data_list(struct urtw_softc *sc, usbd_pipe_handle pipe1,
+ usbd_pipe_handle pipe2, struct urtw_data data[], int ndata)
+{
+ int i;
+
+ /* make sure no transfers are pending */
+ if (pipe1 != NULL)
+ usbd_abort_pipe(pipe1);
+ if (pipe2 != NULL)
+ usbd_abort_pipe(pipe2);
+
+ for (i = 0; i < ndata; i++) {
+ struct urtw_data *dp = &data[i];
+
+ if (dp->xfer != NULL) {
+ usbd_free_xfer(dp->xfer);
+ dp->xfer = NULL;
+ }
+ if (dp->m != NULL) {
+ m_freem(dp->m);
+ dp->m = NULL;
+ }
+ if (dp->ni != NULL) {
+ ieee80211_free_node(dp->ni);
+ dp->ni = NULL;
+ }
+ }
+}
+
+static int
+urtw_alloc_rx_data_list(struct urtw_softc *sc)
+{
+
+ return urtw_alloc_data_list(sc,
+ sc->sc_rxdata, URTW_RX_DATA_LIST_COUNT, MCLBYTES, 1 /* mbufs */);
+}
+
+static void
+urtw_free_rx_data_list(struct urtw_softc *sc)
+{
+
+ urtw_free_data_list(sc, sc->sc_rxpipe, NULL, sc->sc_rxdata,
+ URTW_RX_DATA_LIST_COUNT);
+}
+
+static int
+urtw_alloc_tx_data_list(struct urtw_softc *sc)
+{
+
+ return urtw_alloc_data_list(sc,
+ sc->sc_txdata, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE,
+ 0 /* no mbufs */);
+}
+
+static void
+urtw_free_tx_data_list(struct urtw_softc *sc)
+{
+
+ urtw_free_data_list(sc, sc->sc_txpipe_low, sc->sc_txpipe_normal,
+ sc->sc_txdata, URTW_TX_DATA_LIST_COUNT);
+}
+
+static usbd_status
+urtw_led_init(struct urtw_softc *sc)
+{
+ uint32_t rev;
+ usbd_status error;
+
+ urtw_read8_m(sc, URTW_PSR, &sc->sc_psr);
+ error = urtw_eprom_read32(sc, URTW_EPROM_SWREV, &rev);
+ if (error != 0)
+ goto fail;
+
+ switch (rev & URTW_EPROM_CID_MASK) {
+ case URTW_EPROM_CID_ALPHA0:
+ sc->sc_strategy = URTW_SW_LED_MODE1;
+ break;
+ case URTW_EPROM_CID_SERCOMM_PS:
+ sc->sc_strategy = URTW_SW_LED_MODE3;
+ break;
+ case URTW_EPROM_CID_HW_LED:
+ sc->sc_strategy = URTW_HW_LED;
+ break;
+ case URTW_EPROM_CID_RSVD0:
+ case URTW_EPROM_CID_RSVD1:
+ default:
+ sc->sc_strategy = URTW_SW_LED_MODE0;
+ break;
+ }
+
+ sc->sc_gpio_ledpin = URTW_LED_PIN_GPIO0;
+
+fail:
+ return (error);
+}
+
+/* XXX why we should allocalte memory buffer instead of using memory stack? */
+static usbd_status
+urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index,
+ uint16_t *data)
+{
+ uint8_t *buf;
+ uint16_t data16;
+ usb_device_request_t *req;
+ usbd_status error = 0;
+
+ data16 = *data;
+ req = (usb_device_request_t *)malloc(sizeof(usb_device_request_t),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (req == NULL) {
+ device_printf(sc->sc_dev, "could not allocate a memory\n");
+ goto fail0;
+ }
+ buf = (uint8_t *)malloc(2, M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (req == NULL) {
+ device_printf(sc->sc_dev, "could not allocate a memory\n");
+ goto fail1;
+ }
+
+ req->bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req->bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req->wValue, addr);
+ USETW(req->wIndex, index);
+ USETW(req->wLength, sizeof(uint16_t));
+ buf[0] = (data16 & 0x00ff);
+ buf[1] = (data16 & 0xff00) >> 8;
+
+ error = usbd_do_request(sc->sc_udev, req, buf);
+
+ free(buf, M_80211_VAP);
+fail1: free(req, M_80211_VAP);
+fail0: return (error);
+}
+
+static usbd_status
+urtw_8225_read(struct urtw_softc *sc, uint8_t addr, uint32_t *data)
+{
+ int i;
+ int16_t bit;
+ uint8_t rlen = 12, wlen = 6;
+ uint16_t o1, o2, o3, tmp;
+ uint32_t d2w = ((uint32_t)(addr & 0x1f)) << 27;
+ uint32_t mask = 0x80000000, value = 0;
+ usbd_status error;
+
+ urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &o1);
+ urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &o2);
+ urtw_read16_m(sc, URTW_RF_PINS_SELECT, &o3);
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2 | URTW_RF_PINS_MAGIC4);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | URTW_RF_PINS_MAGIC4);
+ o1 &= ~URTW_RF_PINS_MAGIC4;
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN);
+ DELAY(5);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1);
+ DELAY(5);
+
+ for (i = 0; i < (wlen / 2); i++, mask = mask >> 1) {
+ bit = ((d2w & mask) != 0) ? 1 : 0;
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ mask = mask >> 1;
+ if (i == 2)
+ break;
+ bit = ((d2w & mask) != 0) ? 1 : 0;
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1);
+ DELAY(1);
+ }
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+
+ mask = 0x800;
+ for (i = 0; i < rlen; i++, mask = mask >> 1) {
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+
+ urtw_read16_m(sc, URTW_RF_PINS_INPUT, &tmp);
+ value |= ((tmp & URTW_BB_HOST_BANG_CLK) ? mask : 0);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+ }
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN |
+ URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_OUTPUT_MAGIC1);
+
+ if (data != NULL)
+ *data = value;
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data)
+{
+ uint16_t d80, d82, d84;
+ usbd_status error;
+
+ urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80);
+ d80 &= URTW_RF_PINS_MAGIC1;
+ urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82);
+ urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84);
+ d84 &= URTW_RF_PINS_MAGIC2;
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | URTW_RF_PINS_MAGIC3);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | URTW_RF_PINS_MAGIC3);
+ DELAY(10);
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80);
+ DELAY(10);
+
+ error = urtw_8225_write_s16(sc, addr, 0x8225, &data);
+ if (error != 0)
+ goto fail;
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
+ DELAY(10);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84);
+ usbd_delay_ms(sc->sc_udev, 2);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8225_isv2(struct urtw_softc *sc, int *ret)
+{
+ uint32_t data;
+ usbd_status error;
+
+ *ret = 1;
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_MAGIC5);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, URTW_RF_PINS_MAGIC5);
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, URTW_RF_PINS_MAGIC5);
+ usbd_delay_ms(sc->sc_udev, 500);
+
+ urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC,
+ URTW_8225_ADDR_0_DATA_MAGIC1);
+
+ error = urtw_8225_read(sc, URTW_8225_ADDR_8_MAGIC, &data);
+ if (error != 0)
+ goto fail;
+ if (data != URTW_8225_ADDR_8_DATA_MAGIC1)
+ *ret = 0;
+ else {
+ error = urtw_8225_read(sc, URTW_8225_ADDR_9_MAGIC, &data);
+ if (error != 0)
+ goto fail;
+ if (data != URTW_8225_ADDR_9_DATA_MAGIC1)
+ *ret = 0;
+ }
+
+ urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC,
+ URTW_8225_ADDR_0_DATA_MAGIC2);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_get_rfchip(struct urtw_softc *sc)
+{
+ int ret;
+ uint32_t data;
+ usbd_status error;
+
+ error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data);
+ if (error != 0)
+ goto fail;
+ switch (data & 0xff) {
+ case URTW_EPROM_RFCHIPID_RTL8225U:
+ error = urtw_8225_isv2(sc, &ret);
+ if (error != 0)
+ goto fail;
+ if (ret == 0) {
+ sc->sc_rf_init = urtw_8225_rf_init;
+ sc->sc_rf_set_sens = urtw_8225_rf_set_sens;
+ sc->sc_rf_set_chan = urtw_8225_rf_set_chan;
+ } else {
+ sc->sc_rf_init = urtw_8225v2_rf_init;
+ sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan;
+ }
+ sc->sc_max_sens = URTW_8225_RF_MAX_SENS;
+ sc->sc_sens = URTW_8225_RF_DEF_SENS;
+ break;
+ default:
+ panic("unsupported RF chip %d\n", data & 0xff);
+ /* never reach */
+ }
+
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_get_txpwr(struct urtw_softc *sc)
+{
+ int i, j;
+ uint32_t data;
+ usbd_status error;
+
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW_BASE, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck_base = data & 0xf;
+ sc->sc_txpwr_ofdm_base = (data >> 4) & 0xf;
+
+ for (i = 1, j = 0; i < 6; i += 2, j++) {
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW0 + j, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[i] = data & 0xf;
+ sc->sc_txpwr_cck[i + 1] = (data & 0xf00) >> 8;
+ sc->sc_txpwr_ofdm[i] = (data & 0xf0) >> 4;
+ sc->sc_txpwr_ofdm[i + 1] = (data & 0xf000) >> 12;
+ }
+ for (i = 1, j = 0; i < 4; i += 2, j++) {
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW1 + j, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[i + 6] = data & 0xf;
+ sc->sc_txpwr_cck[i + 6 + 1] = (data & 0xf00) >> 8;
+ sc->sc_txpwr_ofdm[i + 6] = (data & 0xf0) >> 4;
+ sc->sc_txpwr_ofdm[i + 6 + 1] = (data & 0xf000) >> 12;
+ }
+ for (i = 1, j = 0; i < 4; i += 2, j++) {
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf;
+ sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8;
+ sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4;
+ sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12;
+ }
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_get_macaddr(struct urtw_softc *sc)
+{
+ uint32_t data;
+ usbd_status error;
+
+ error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_bssid[0] = data & 0xff;
+ sc->sc_bssid[1] = (data & 0xff00) >> 8;
+ error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_bssid[2] = data & 0xff;
+ sc->sc_bssid[3] = (data & 0xff00) >> 8;
+ error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_bssid[4] = data & 0xff;
+ sc->sc_bssid[5] = (data & 0xff00) >> 8;
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_eprom_read32(struct urtw_softc *sc, uint32_t addr, uint32_t *data)
+{
+#define URTW_READCMD_LEN 3
+ int addrlen, i;
+ int16_t addrstr[8], data16, readcmd[] = { 1, 1, 0 };
+ usbd_status error;
+
+ /* NB: make sure the buffer is initialized */
+ *data = 0;
+
+ /* enable EPROM programming */
+ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_PROGRAM_MODE);
+ DELAY(URTW_EPROM_DELAY);
+
+ error = urtw_eprom_cs(sc, URTW_EPROM_ENABLE);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_sendbits(sc, readcmd, URTW_READCMD_LEN);
+ if (error != 0)
+ goto fail;
+ if (sc->sc_epromtype == URTW_EEPROM_93C56) {
+ addrlen = 8;
+ addrstr[0] = addr & (1 << 7);
+ addrstr[1] = addr & (1 << 6);
+ addrstr[2] = addr & (1 << 5);
+ addrstr[3] = addr & (1 << 4);
+ addrstr[4] = addr & (1 << 3);
+ addrstr[5] = addr & (1 << 2);
+ addrstr[6] = addr & (1 << 1);
+ addrstr[7] = addr & (1 << 0);
+ } else {
+ addrlen=6;
+ addrstr[0] = addr & (1 << 5);
+ addrstr[1] = addr & (1 << 4);
+ addrstr[2] = addr & (1 << 3);
+ addrstr[3] = addr & (1 << 2);
+ addrstr[4] = addr & (1 << 1);
+ addrstr[5] = addr & (1 << 0);
+ }
+ error = urtw_eprom_sendbits(sc, addrstr, addrlen);
+ if (error != 0)
+ goto fail;
+
+ error = urtw_eprom_writebit(sc, 0);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < 16; i++) {
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_readbit(sc, &data16);
+ if (error != 0)
+ goto fail;
+
+ (*data) |= (data16 << (15 - i));
+ }
+
+ error = urtw_eprom_cs(sc, URTW_EPROM_DISABLE);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+
+ /* now disable EPROM programming */
+ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_NORMAL_MODE);
+fail:
+ return (error);
+#undef URTW_READCMD_LEN
+}
+
+static usbd_status
+urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data)
+{
+ uint8_t data8;
+ usbd_status error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data8);
+ *data = (data8 & URTW_EPROM_READBIT) ? 1 : 0;
+ DELAY(URTW_EPROM_DELAY);
+
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen)
+{
+ int i = 0;
+ usbd_status error = 0;
+
+ for (i = 0; i < buflen; i++) {
+ error = urtw_eprom_writebit(sc, buf[i]);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+ }
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit)
+{
+ uint8_t data;
+ usbd_status error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ if (bit != 0)
+ urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_WRITEBIT);
+ else
+ urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_WRITEBIT);
+ DELAY(URTW_EPROM_DELAY);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_eprom_ck(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usbd_status error;
+
+ /* masking */
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CK);
+ DELAY(URTW_EPROM_DELAY);
+ /* unmasking */
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CK);
+ DELAY(URTW_EPROM_DELAY);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_eprom_cs(struct urtw_softc *sc, int able)
+{
+ uint8_t data;
+ usbd_status error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ if (able == URTW_EPROM_ENABLE)
+ urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CS);
+ else
+ urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CS);
+ DELAY(URTW_EPROM_DELAY);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, val | 0xff00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ error = usbd_do_request(sc->sc_udev, &req, data);
+ return (error);
+}
+
+static usbd_status
+urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, val | 0xfe00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ error = usbd_do_request(sc->sc_udev, &req, data);
+ return (error);
+}
+
+static usbd_status
+urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, val | 0xff00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint16_t));
+
+ error = usbd_do_request(sc->sc_udev, &req, data);
+ return (error);
+}
+
+static usbd_status
+urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, val | 0xff00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint32_t));
+
+ error = usbd_do_request(sc->sc_udev, &req, data);
+ return (error);
+}
+
+static usbd_status
+urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, val | 0xff00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ return (usbd_do_request(sc->sc_udev, &req, &data));
+}
+
+static usbd_status
+urtw_write8e(struct urtw_softc *sc, int val, uint8_t data)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, val | 0xfe00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ return (usbd_do_request(sc->sc_udev, &req, &data));
+}
+
+static usbd_status
+urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, val | 0xff00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint16_t));
+
+ return (usbd_do_request(sc->sc_udev, &req, &data));
+}
+
+static usbd_status
+urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, val | 0xff00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint32_t));
+
+ return (usbd_do_request(sc->sc_udev, &req, &data));
+}
+
+static int
+urtw_detach(device_t dev)
+{
+ struct urtw_softc *sc = device_get_softc(dev);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ if (!device_is_attached(dev))
+ return 0;
+
+ urtw_stop(ifp, 1);
+
+ callout_drain(&sc->sc_led_ch);
+ callout_drain(&sc->sc_watchdog_ch);
+ usb_rem_task(sc->sc_udev, &sc->sc_ledtask);
+ usb_rem_task(sc->sc_udev, &sc->sc_ctxtask);
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+
+ /* abort and free xfers */
+ urtw_free_tx_data_list(sc);
+ urtw_free_rx_data_list(sc);
+ urtw_close_pipes(sc);
+
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+ if_free(ifp);
+ mtx_destroy(&sc->sc_mtx);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+urtw_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct urtw_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return (NULL);
+ uvp = (struct urtw_vap *) malloc(sizeof(struct urtw_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (uvp == NULL)
+ return (NULL);
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = urtw_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return (vap);
+}
+
+static void
+urtw_vap_delete(struct ieee80211vap *vap)
+{
+ struct urtw_vap *uvp = URTW_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static usbd_status
+urtw_set_mode(struct urtw_softc *sc, uint32_t mode)
+{
+ uint8_t data;
+ usbd_status error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ data = (data & ~URTW_EPROM_CMD_MASK) | (mode << URTW_EPROM_CMD_SHIFT);
+ data = data & ~(URTW_EPROM_CS | URTW_EPROM_CK);
+ urtw_write8_m(sc, URTW_EPROM_CMD, data);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val)
+{
+ uint8_t data;
+ usbd_status error;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
+ urtw_write32_m(sc, URTW_ANAPARAM, val);
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val)
+{
+ uint8_t data;
+ usbd_status error;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
+ urtw_write32_m(sc, URTW_ANAPARAM2, val);
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_intr_disable(struct urtw_softc *sc)
+{
+ usbd_status error;
+
+ urtw_write16_m(sc, URTW_INTR_MASK, 0);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_reset(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usbd_status error;
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+
+ error = urtw_intr_disable(sc);
+ if (error)
+ goto fail;
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ error = urtw_write8e(sc, 0x18, 0x10);
+ if (error != 0)
+ goto fail;
+ error = urtw_write8e(sc, 0x18, 0x11);
+ if (error != 0)
+ goto fail;
+ error = urtw_write8e(sc, 0x18, 0x00);
+ if (error != 0)
+ goto fail;
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ urtw_read8_m(sc, URTW_CMD, &data);
+ data = (data & 0x2) | URTW_CMD_RST;
+ urtw_write8_m(sc, URTW_CMD, data);
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ urtw_read8_m(sc, URTW_CMD, &data);
+ if (data & URTW_CMD_RST) {
+ device_printf(sc->sc_dev, "reset timeout\n");
+ goto fail;
+ }
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD);
+ if (error)
+ goto fail;
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_led_on(struct urtw_softc *sc, int type)
+{
+ usbd_status error;
+
+ if (type == URTW_LED_GPIO) {
+ switch (sc->sc_gpio_ledpin) {
+ case URTW_LED_PIN_GPIO0:
+ urtw_write8_m(sc, URTW_GPIO, 0x01);
+ urtw_write8_m(sc, URTW_GP_ENABLE, 0x00);
+ break;
+ default:
+ panic("unsupported LED PIN type 0x%x",
+ sc->sc_gpio_ledpin);
+ /* never reach */
+ }
+ } else {
+ panic("unsupported LED type 0x%x", type);
+ /* never reach */
+ }
+
+ sc->sc_gpio_ledon = 1;
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_led_off(struct urtw_softc *sc, int type)
+{
+ usbd_status error;
+
+ if (type == URTW_LED_GPIO) {
+ switch (sc->sc_gpio_ledpin) {
+ case URTW_LED_PIN_GPIO0:
+ urtw_write8_m(sc, URTW_GPIO, URTW_GPIO_DATA_MAGIC1);
+ urtw_write8_m(sc,
+ URTW_GP_ENABLE, URTW_GP_ENABLE_DATA_MAGIC1);
+ break;
+ default:
+ panic("unsupported LED PIN type 0x%x",
+ sc->sc_gpio_ledpin);
+ /* never reach */
+ }
+ } else {
+ panic("unsupported LED type 0x%x", type);
+ /* never reach */
+ }
+
+ sc->sc_gpio_ledon = 0;
+
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_led_mode0(struct urtw_softc *sc, int mode)
+{
+
+ switch (mode) {
+ case URTW_LED_CTL_POWER_ON:
+ sc->sc_gpio_ledstate = URTW_LED_POWER_ON_BLINK;
+ break;
+ case URTW_LED_CTL_TX:
+ if (sc->sc_gpio_ledinprogress == 1)
+ return (0);
+
+ sc->sc_gpio_ledstate = URTW_LED_BLINK_NORMAL;
+ sc->sc_gpio_blinktime = 2;
+ break;
+ case URTW_LED_CTL_LINK:
+ sc->sc_gpio_ledstate = URTW_LED_ON;
+ break;
+ default:
+ panic("unsupported LED mode 0x%x", mode);
+ /* never reach */
+ }
+
+ switch (sc->sc_gpio_ledstate) {
+ case URTW_LED_ON:
+ if (sc->sc_gpio_ledinprogress != 0)
+ break;
+ urtw_led_on(sc, URTW_LED_GPIO);
+ break;
+ case URTW_LED_BLINK_NORMAL:
+ if (sc->sc_gpio_ledinprogress != 0)
+ break;
+ sc->sc_gpio_ledinprogress = 1;
+ sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ?
+ URTW_LED_OFF : URTW_LED_ON;
+ callout_reset(&sc->sc_led_ch, hz, urtw_ledtask, sc);
+ break;
+ case URTW_LED_POWER_ON_BLINK:
+ urtw_led_on(sc, URTW_LED_GPIO);
+ usbd_delay_ms(sc->sc_udev, 100);
+ urtw_led_off(sc, URTW_LED_GPIO);
+ break;
+ default:
+ panic("unknown LED status 0x%x", sc->sc_gpio_ledstate);
+ /* never reach */
+ }
+ return (0);
+}
+
+static usbd_status
+urtw_led_mode1(struct urtw_softc *sc, int mode)
+{
+
+ return (USBD_INVAL);
+}
+
+static usbd_status
+urtw_led_mode2(struct urtw_softc *sc, int mode)
+{
+
+ return (USBD_INVAL);
+}
+
+static usbd_status
+urtw_led_mode3(struct urtw_softc *sc, int mode)
+{
+
+ return (USBD_INVAL);
+}
+
+static usbd_status
+urtw_led_blink(struct urtw_softc *sc)
+{
+ uint8_t ing = 0;
+ usbd_status error;
+
+ if (sc->sc_gpio_blinkstate == URTW_LED_ON)
+ error = urtw_led_on(sc, URTW_LED_GPIO);
+ else
+ error = urtw_led_off(sc, URTW_LED_GPIO);
+ sc->sc_gpio_blinktime--;
+ if (sc->sc_gpio_blinktime == 0)
+ ing = 1;
+ else {
+ if (sc->sc_gpio_ledstate != URTW_LED_BLINK_NORMAL &&
+ sc->sc_gpio_ledstate != URTW_LED_BLINK_SLOWLY &&
+ sc->sc_gpio_ledstate != URTW_LED_BLINK_CM3)
+ ing = 1;
+ }
+ if (ing == 1) {
+ if (sc->sc_gpio_ledstate == URTW_LED_ON &&
+ sc->sc_gpio_ledon == 0)
+ error = urtw_led_on(sc, URTW_LED_GPIO);
+ else if (sc->sc_gpio_ledstate == URTW_LED_OFF &&
+ sc->sc_gpio_ledon == 1)
+ error = urtw_led_off(sc, URTW_LED_GPIO);
+
+ sc->sc_gpio_blinktime = 0;
+ sc->sc_gpio_ledinprogress = 0;
+ return (0);
+ }
+
+ sc->sc_gpio_blinkstate = (sc->sc_gpio_blinkstate != URTW_LED_ON) ?
+ URTW_LED_ON : URTW_LED_OFF;
+
+ switch (sc->sc_gpio_ledstate) {
+ case URTW_LED_BLINK_NORMAL:
+ callout_reset(&sc->sc_led_ch, hz, urtw_ledtask, sc);
+ break;
+ default:
+ panic("unknown LED status 0x%x", sc->sc_gpio_ledstate);
+ /* never reach */
+ }
+ return (0);
+}
+
+static void
+urtw_ledusbtask(void *arg)
+{
+ struct urtw_softc *sc = arg;
+
+ if (sc->sc_strategy != URTW_SW_LED_MODE0)
+ panic("could not process a LED strategy 0x%x", sc->sc_strategy);
+
+ urtw_led_blink(sc);
+}
+
+static void
+urtw_ledtask(void *arg)
+{
+ struct urtw_softc *sc = arg;
+
+ /*
+ * NB: to change a status of the led we need at least a sleep so we
+ * can't do it here
+ */
+ usb_add_task(sc->sc_udev, &sc->sc_ledtask, USB_TASKQ_DRIVER);
+}
+
+static usbd_status
+urtw_led_ctl(struct urtw_softc *sc, int mode)
+{
+ usbd_status error = 0;
+
+ switch (sc->sc_strategy) {
+ case URTW_SW_LED_MODE0:
+ error = urtw_led_mode0(sc, mode);
+ break;
+ case URTW_SW_LED_MODE1:
+ error = urtw_led_mode1(sc, mode);
+ break;
+ case URTW_SW_LED_MODE2:
+ error = urtw_led_mode2(sc, mode);
+ break;
+ case URTW_SW_LED_MODE3:
+ error = urtw_led_mode3(sc, mode);
+ break;
+ default:
+ panic("unsupported LED mode %d\n", sc->sc_strategy);
+ /* never reach */
+ }
+
+ return (error);
+}
+
+static usbd_status
+urtw_update_msr(struct urtw_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint8_t data;
+ usbd_status error;
+
+ urtw_read8_m(sc, URTW_MSR, &data);
+ data &= ~URTW_MSR_LINK_MASK;
+
+ if (sc->sc_state == IEEE80211_S_RUN) {
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_MONITOR:
+ data |= URTW_MSR_LINK_STA;
+ break;
+ case IEEE80211_M_IBSS:
+ data |= URTW_MSR_LINK_ADHOC;
+ break;
+ case IEEE80211_M_HOSTAP:
+ data |= URTW_MSR_LINK_HOSTAP;
+ break;
+ default:
+ panic("unsupported operation mode 0x%x\n",
+ ic->ic_opmode);
+ /* never reach */
+ }
+ } else
+ data |= URTW_MSR_LINK_NONE;
+
+ urtw_write8_m(sc, URTW_MSR, data);
+fail:
+ return (error);
+}
+
+static uint16_t
+urtw_rate2rtl(int rate)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int i;
+
+ for (i = 0; i < N(urtw_ratetable); i++) {
+ if (rate == urtw_ratetable[i].reg)
+ return urtw_ratetable[i].val;
+ }
+
+ return (3);
+#undef N
+}
+
+static uint16_t
+urtw_rtl2rate(int rate)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int i;
+
+ for (i = 0; i < N(urtw_ratetable); i++) {
+ if (rate == urtw_ratetable[i].val)
+ return urtw_ratetable[i].reg;
+ }
+
+ return (0);
+#undef N
+}
+
+static usbd_status
+urtw_set_rate(struct urtw_softc *sc)
+{
+ int i, basic_rate, min_rr_rate, max_rr_rate;
+ uint16_t data;
+ usbd_status error;
+
+ basic_rate = urtw_rate2rtl(48);
+ min_rr_rate = urtw_rate2rtl(12);
+ max_rr_rate = urtw_rate2rtl(48);
+
+ urtw_write8_m(sc, URTW_RESP_RATE,
+ max_rr_rate << URTW_RESP_MAX_RATE_SHIFT |
+ min_rr_rate << URTW_RESP_MIN_RATE_SHIFT);
+
+ urtw_read16_m(sc, URTW_BRSR, &data);
+ data &= ~URTW_BRSR_MBR_8185;
+
+ for (i = 0; i <= basic_rate; i++)
+ data |= (1 << i);
+
+ urtw_write16_m(sc, URTW_BRSR, data);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_intr_enable(struct urtw_softc *sc)
+{
+ usbd_status error;
+
+ urtw_write16_m(sc, URTW_INTR_MASK, 0xffff);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_adapter_start(struct urtw_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ usbd_status error;
+
+ error = urtw_reset(sc);
+ if (error)
+ goto fail;
+
+ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 0);
+ urtw_write8_m(sc, URTW_GPIO, 0);
+
+ /* for led */
+ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4);
+ error = urtw_led_ctl(sc, URTW_LED_CTL_POWER_ON);
+ if (error != 0)
+ goto fail;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ /* applying MAC address again. */
+ urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_myaddr)[0]);
+ urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_myaddr)[1] & 0xffff);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ error = urtw_update_msr(sc);
+ if (error)
+ goto fail;
+
+ urtw_write32_m(sc, URTW_INT_TIMEOUT, 0);
+ urtw_write8_m(sc, URTW_WPA_CONFIG, 0);
+ urtw_write8_m(sc, URTW_RATE_FALLBACK, 0x81);
+ error = urtw_set_rate(sc);
+ if (error != 0)
+ goto fail;
+
+ error = sc->sc_rf_init(sc);
+ if (error != 0)
+ goto fail;
+ if (sc->sc_rf_set_sens != NULL)
+ sc->sc_rf_set_sens(sc, sc->sc_sens);
+
+ /* XXX correct? to call write16 */
+ urtw_write16_m(sc, URTW_PSR, 1);
+ urtw_write16_m(sc, URTW_ADDR_MAGIC2, 0x10);
+ urtw_write8_m(sc, URTW_TALLY_SEL, 0x80);
+ urtw_write8_m(sc, URTW_ADDR_MAGIC3, 0x60);
+ /* XXX correct? to call write16 */
+ urtw_write16_m(sc, URTW_PSR, 0);
+ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4);
+
+ error = urtw_intr_enable(sc);
+ if (error != 0)
+ goto fail;
+
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_rx_setconf(struct urtw_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t data;
+ usbd_status error;
+
+ urtw_read32_m(sc, URTW_RX, &data);
+ data = data &~ URTW_RX_FILTER_MASK;
+#if 0
+ data = data | URTW_RX_FILTER_CTL;
+#endif
+ data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA;
+ data = data | URTW_RX_FILTER_BCAST | URTW_RX_FILTER_MCAST;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ data = data | URTW_RX_FILTER_ICVERR;
+ data = data | URTW_RX_FILTER_PWR;
+ }
+ if (sc->sc_crcmon == 1 && ic->ic_opmode == IEEE80211_M_MONITOR)
+ data = data | URTW_RX_FILTER_CRCERR;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+ (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) {
+ data = data | URTW_RX_FILTER_ALLMAC;
+ } else {
+ data = data | URTW_RX_FILTER_NICMAC;
+ data = data | URTW_RX_CHECK_BSSID;
+ }
+
+ data = data &~ URTW_RX_FIFO_THRESHOLD_MASK;
+ data = data | URTW_RX_FIFO_THRESHOLD_NONE | URTW_RX_AUTORESETPHY;
+ data = data &~ URTW_MAX_RX_DMA_MASK;
+ data = data | URTW_MAX_RX_DMA_2048 | URTW_RCR_ONLYERLPKT;
+
+ urtw_write32_m(sc, URTW_RX, data);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_rx_enable(struct urtw_softc *sc)
+{
+ int i;
+ struct urtw_data *rxdata;
+ uint8_t data;
+ usbd_status error;
+
+ /*
+ * Start up the receive pipe.
+ */
+ for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) {
+ rxdata = &sc->sc_rxdata[i];
+
+ usbd_setup_xfer(rxdata->xfer, sc->sc_rxpipe, rxdata,
+ rxdata->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+ urtw_rxeof);
+ error = usbd_transfer(rxdata->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ device_printf(sc->sc_dev,
+ "could not queue Rx transfer\n");
+ goto fail;
+ }
+ }
+
+ error = urtw_rx_setconf(sc);
+ if (error != 0)
+ goto fail;
+
+ urtw_read8_m(sc, URTW_CMD, &data);
+ urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_tx_enable(struct urtw_softc *sc)
+{
+ uint8_t data8;
+ uint32_t data;
+ usbd_status error;
+
+ urtw_read8_m(sc, URTW_CW_CONF, &data8);
+ data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY);
+ urtw_write8_m(sc, URTW_CW_CONF, data8);
+
+ urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8);
+ data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN;
+ data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL;
+ data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT;
+ urtw_write8_m(sc, URTW_TX_AGC_CTL, data8);
+
+ urtw_read32_m(sc, URTW_TX_CONF, &data);
+ data &= ~URTW_TX_LOOPBACK_MASK;
+ data |= URTW_TX_LOOPBACK_NONE;
+ data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK);
+ data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT;
+ data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT;
+ data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK);
+ data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW;
+ data &= ~URTW_TX_SWPLCPLEN;
+ data |= URTW_TX_NOICV;
+ urtw_write32_m(sc, URTW_TX_CONF, data);
+
+ urtw_read8_m(sc, URTW_CMD, &data8);
+ urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE);
+fail:
+ return (error);
+}
+
+static void
+urtw_init(void *arg)
+{
+ int ret;
+ struct urtw_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ usbd_status error;
+
+ urtw_stop(ifp, 0);
+
+ error = urtw_adapter_start(sc);
+ if (error != 0)
+ goto fail;
+
+ /* reset softc variables */
+ sc->sc_txidx = sc->sc_tx_low_queued = sc->sc_tx_normal_queued = 0;
+ sc->sc_txtimer = 0;
+
+ if (!(sc->sc_flags & URTW_INIT_ONCE)) {
+ error = usbd_set_config_no(sc->sc_udev, URTW_CONFIG_NO, 0);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not set configuration no\n");
+ goto fail;
+ }
+ /* get the first interface handle */
+ error = usbd_device2interface_handle(sc->sc_udev,
+ URTW_IFACE_INDEX, &sc->sc_iface);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not get interface handle\n");
+ goto fail;
+ }
+ error = urtw_open_pipes(sc);
+ if (error != 0)
+ goto fail;
+ ret = urtw_alloc_rx_data_list(sc);
+ if (error != 0)
+ goto fail;
+ ret = urtw_alloc_tx_data_list(sc);
+ if (error != 0)
+ goto fail;
+ sc->sc_flags |= URTW_INIT_ONCE;
+ }
+
+ error = urtw_rx_enable(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_tx_enable(sc);
+ if (error != 0)
+ goto fail;
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
+fail:
+ return;
+}
+
+static void
+urtw_set_multi(void *arg)
+{
+ struct urtw_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if (!(ifp->if_flags & IFF_UP))
+ return;
+
+ /*
+ * XXX don't know how to set a device. Lack of docs. Just try to set
+ * IFF_ALLMULTI flag here.
+ */
+ IF_ADDR_LOCK(ifp);
+ ifp->if_flags |= IFF_ALLMULTI;
+ IF_ADDR_UNLOCK(ifp);
+}
+
+static int
+urtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct urtw_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ mtx_lock(&Giant);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ sc->sc_if_flags) &
+ (IFF_ALLMULTI | IFF_PROMISC))
+ urtw_set_multi(sc);
+ } else {
+ urtw_init(ifp->if_softc);
+ startall = 1;
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ urtw_stop(ifp, 1);
+ }
+ sc->sc_if_flags = ifp->if_flags;
+ mtx_unlock(&Giant);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCGIFADDR:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+static void
+urtw_start(struct ifnet *ifp)
+{
+ struct urtw_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ URTW_LOCK(sc);
+ for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->sc_tx_low_queued >= URTW_TX_DATA_LIST_COUNT ||
+ sc->sc_tx_normal_queued >= URTW_TX_DATA_LIST_COUNT) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+
+ if (urtw_tx_start(sc, ni, m, URTW_PRIORITY_NORMAL) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
+
+ sc->sc_txtimer = 5;
+ }
+ URTW_UNLOCK(sc);
+}
+
+static void
+urtw_txeof_low(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct urtw_data *data = priv;
+ struct urtw_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct mbuf *m;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_txpipe_low);
+
+ ifp->if_oerrors++;
+ return;
+ }
+
+ /*
+ * Do any tx complete callback. Note this must be done before releasing
+ * the node reference.
+ */
+ m = data->m;
+ if (m != NULL && m->m_flags & M_TXCB) {
+ ieee80211_process_callback(data->ni, m, 0); /* XXX status? */
+ m_freem(m);
+ data->m = NULL;
+ }
+
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+
+ sc->sc_txtimer = 0;
+ ifp->if_opackets++;
+
+ URTW_LOCK(sc);
+ sc->sc_tx_low_queued--;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ URTW_UNLOCK(sc);
+
+ urtw_start(ifp);
+}
+
+static void
+urtw_txeof_normal(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct urtw_data *data = priv;
+ struct urtw_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct mbuf *m;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_txpipe_normal);
+
+ ifp->if_oerrors++;
+ return;
+ }
+
+ /*
+ * Do any tx complete callback. Note this must be done before releasing
+ * the node reference.
+ */
+ m = data->m;
+ if (m != NULL && m->m_flags & M_TXCB) {
+ ieee80211_process_callback(data->ni, m, 0); /* XXX status? */
+ m_freem(m);
+ data->m = NULL;
+ }
+
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+
+ sc->sc_txtimer = 0;
+ ifp->if_opackets++;
+
+ URTW_LOCK(sc);
+ sc->sc_tx_normal_queued--;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ URTW_UNLOCK(sc);
+
+ urtw_start(ifp);
+}
+
+static int
+urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0,
+ int prior)
+{
+ int xferlen;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
+ struct ieee80211_key *k;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct urtw_data *data;
+ usbd_status error;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ /*
+ * Software crypto.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ device_printf(sc->sc_dev,
+ "ieee80211_crypto_encap returns NULL.\n");
+ /* XXX we don't expect the fragmented frames */
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+
+ /* in case packet header moved, reset pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct urtw_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ /* XXX Are variables correct? */
+ tap->wt_flags = 0;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ xferlen = m0->m_pkthdr.len + 4 * 3;
+ if((0 == xferlen % 64) || (0 == xferlen % 512))
+ xferlen += 1;
+
+ data = &sc->sc_txdata[sc->sc_txidx];
+ sc->sc_txidx = (sc->sc_txidx + 1) % URTW_TX_DATA_LIST_COUNT;
+
+ bzero(data->buf, URTW_TX_MAXSIZE);
+ data->buf[0] = m0->m_pkthdr.len & 0xff;
+ data->buf[1] = (m0->m_pkthdr.len & 0x0f00) >> 8;
+ data->buf[1] |= (1 << 7);
+
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) &&
+ (sc->sc_preamble_mode == URTW_PREAMBLE_MODE_SHORT) &&
+ (sc->sc_currate != 0))
+ data->buf[2] |= 1;
+ if ((m0->m_pkthdr.len > vap->iv_rtsthreshold) &&
+ prior == URTW_PRIORITY_LOW)
+ panic("TODO tx.");
+ if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
+ data->buf[2] |= (1 << 1);
+ /* RTS rate - 10 means we use a basic rate. */
+ data->buf[2] |= (urtw_rate2rtl(2) << 3);
+ /*
+ * XXX currently TX rate control depends on the rate value of
+ * RX descriptor because I don't know how to we can control TX rate
+ * in more smart way. Please fix me you find a thing.
+ */
+ data->buf[3] = sc->sc_currate;
+ if (prior == URTW_PRIORITY_NORMAL) {
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ data->buf[3] = urtw_rate2rtl(tp->mcastrate);
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ data->buf[3] = urtw_rate2rtl(tp->ucastrate);
+ }
+ data->buf[8] = 3; /* CW minimum */
+ data->buf[8] |= (7 << 4); /* CW maximum */
+ data->buf[9] |= 11; /* retry limitation */
+
+ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)&data->buf[12]);
+ data->ni = ni;
+ data->m = m0;
+
+ usbd_setup_xfer(data->xfer,
+ (prior == URTW_PRIORITY_LOW) ? sc->sc_txpipe_low :
+ sc->sc_txpipe_normal, data, data->buf, xferlen,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, URTW_DATA_TIMEOUT,
+ (prior == URTW_PRIORITY_LOW) ? urtw_txeof_low : urtw_txeof_normal);
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_IN_PROGRESS && error != USBD_NORMAL_COMPLETION) {
+ device_printf(sc->sc_dev, "could not send frame: %s\n",
+ usbd_errstr(error));
+ return EIO;
+ }
+
+ error = urtw_led_ctl(sc, URTW_LED_CTL_TX);
+ if (error != 0)
+ device_printf(sc->sc_dev, "could not control LED (%d)\n", error);
+
+ if (prior == URTW_PRIORITY_LOW)
+ sc->sc_tx_low_queued++;
+ else
+ sc->sc_tx_normal_queued++;
+
+ return (0);
+}
+
+static int
+urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct urtw_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ URTW_LOCK(sc);
+ if (sc->sc_tx_low_queued >= URTW_TX_DATA_LIST_COUNT ||
+ sc->sc_tx_normal_queued >= URTW_TX_DATA_LIST_COUNT) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ URTW_UNLOCK(sc);
+ return (ENOBUFS); /* XXX */
+ }
+
+ ifp->if_opackets++;
+ if (urtw_tx_start(sc, ni, m, URTW_PRIORITY_LOW) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ URTW_UNLOCK(sc);
+ return (EIO);
+ }
+
+ sc->sc_txtimer = 5;
+ URTW_UNLOCK(sc);
+ return (0);
+}
+
+static void
+urtw_scan_start(struct ieee80211com *ic)
+{
+
+ /* XXX do nothing? */
+}
+
+static void
+urtw_scan_end(struct ieee80211com *ic)
+{
+
+ /* XXX do nothing? */
+}
+
+static void
+urtw_set_channel(struct ieee80211com *ic)
+{
+ struct urtw_softc *sc = ic->ic_ifp->if_softc;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ /*
+ * if the user set a channel explicitly using ifconfig(8) this function
+ * can be called earlier than we're expected that in some cases the
+ * initialization would be failed if setting a channel is called before
+ * the init have done.
+ */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ return;
+
+ sc->sc_ctxarg = URTW_SET_CHANNEL;
+ usb_add_task(sc->sc_udev, &sc->sc_ctxtask, USB_TASKQ_DRIVER);
+}
+
+static void
+urtw_update_mcast(struct ifnet *ifp)
+{
+
+ /* XXX do nothing? */
+}
+
+static usbd_status
+urtw_8225_usb_init(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usbd_status error;
+
+ urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 0);
+ urtw_write8_m(sc, URTW_GPIO, 0);
+ error = urtw_read8e(sc, 0x53, &data);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x53, data | (1 << 7));
+ if (error)
+ goto fail;
+ urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 4);
+ urtw_write8_m(sc, URTW_GPIO, 0x20);
+ urtw_write8_m(sc, URTW_GP_ENABLE, 0);
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x80);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x80);
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x80);
+
+ usbd_delay_ms(sc->sc_udev, 500);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8185_rf_pins_enable(struct urtw_softc *sc)
+{
+ usbd_status error = 0;
+
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data)
+{
+ uint32_t phyw;
+ usbd_status error;
+
+ phyw = ((data << 8) | (addr | 0x80));
+ urtw_write8_m(sc, URTW_PHY_MAGIC4, ((phyw & 0xff000000) >> 24));
+ urtw_write8_m(sc, URTW_PHY_MAGIC3, ((phyw & 0x00ff0000) >> 16));
+ urtw_write8_m(sc, URTW_PHY_MAGIC2, ((phyw & 0x0000ff00) >> 8));
+ urtw_write8_m(sc, URTW_PHY_MAGIC1, ((phyw & 0x000000ff)));
+ usbd_delay_ms(sc->sc_udev, 1);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8187_write_phy_ofdm_c(struct urtw_softc *sc, uint8_t addr, uint32_t data)
+{
+
+ data = data & 0xff;
+ return urtw_8187_write_phy(sc, addr, data);
+}
+
+static usbd_status
+urtw_8187_write_phy_cck_c(struct urtw_softc *sc, uint8_t addr, uint32_t data)
+{
+
+ data = data & 0xff;
+ return urtw_8187_write_phy(sc, addr, data | 0x10000);
+}
+
+static usbd_status
+urtw_8225_setgain(struct urtw_softc *sc, int16_t gain)
+{
+ usbd_status error;
+
+ urtw_8187_write_phy_ofdm(sc, 0x0d, urtw_8225_gain[gain * 4]);
+ urtw_8187_write_phy_ofdm(sc, 0x1b, urtw_8225_gain[gain * 4 + 2]);
+ urtw_8187_write_phy_ofdm(sc, 0x1d, urtw_8225_gain[gain * 4 + 3]);
+ urtw_8187_write_phy_ofdm(sc, 0x23, urtw_8225_gain[gain * 4 + 1]);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8225_set_txpwrlvl(struct urtw_softc *sc, int chan)
+{
+ int i, idx, set;
+ uint8_t *cck_pwltable;
+ uint8_t cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max;
+ uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
+ uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
+ usbd_status error;
+
+ cck_pwrlvl_max = 11;
+ ofdm_pwrlvl_max = 25; /* 12 -> 25 */
+ ofdm_pwrlvl_min = 10;
+
+ /* CCK power setting */
+ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl;
+ idx = cck_pwrlvl % 6;
+ set = cck_pwrlvl / 6;
+ cck_pwltable = (chan == 14) ? urtw_8225_txpwr_cck_ch14 :
+ urtw_8225_txpwr_cck;
+
+ urtw_write8_m(sc, URTW_TX_GAIN_CCK,
+ urtw_8225_tx_gain_cck_ofdm[set] >> 1);
+ for (i = 0; i < 8; i++) {
+ urtw_8187_write_phy_cck(sc, 0x44 + i,
+ cck_pwltable[idx * 8 + i]);
+ }
+ usbd_delay_ms(sc->sc_udev, 1);
+
+ /* OFDM power setting */
+ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ?
+ ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min;
+ ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
+
+ idx = ofdm_pwrlvl % 6;
+ set = ofdm_pwrlvl / 6;
+
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+ urtw_8187_write_phy_ofdm(sc, 2, 0x42);
+ urtw_8187_write_phy_ofdm(sc, 6, 0);
+ urtw_8187_write_phy_ofdm(sc, 8, 0);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
+ urtw_8225_tx_gain_cck_ofdm[set] >> 1);
+ urtw_8187_write_phy_ofdm(sc, 0x5, urtw_8225_txpwr_ofdm[idx]);
+ urtw_8187_write_phy_ofdm(sc, 0x7, urtw_8225_txpwr_ofdm[idx]);
+ usbd_delay_ms(sc->sc_udev, 1);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant)
+{
+ usbd_status error;
+
+ urtw_write8_m(sc, URTW_TX_ANTENNA, ant);
+ usbd_delay_ms(sc->sc_udev, 1);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8225_rf_init(struct urtw_softc *sc)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int i;
+ uint16_t data;
+ usbd_status error;
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+
+ error = urtw_8225_usb_init(sc);
+ if (error)
+ goto fail;
+
+ urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008);
+ urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */
+ urtw_write16_m(sc, URTW_BRSR, 0xffff);
+ urtw_write32_m(sc, URTW_RF_PARA, 0x100044);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ urtw_write8_m(sc, URTW_CONFIG3, 0x44);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ error = urtw_8185_rf_pins_enable(sc);
+ if (error)
+ goto fail;
+ usbd_delay_ms(sc->sc_udev, 1000);
+
+ for (i = 0; i < N(urtw_8225_rf_part1); i++) {
+ urtw_8225_write(sc, urtw_8225_rf_part1[i].reg,
+ urtw_8225_rf_part1[i].val);
+ usbd_delay_ms(sc->sc_udev, 1);
+ }
+ usbd_delay_ms(sc->sc_udev, 100);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
+ usbd_delay_ms(sc->sc_udev, 200);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
+ usbd_delay_ms(sc->sc_udev, 200);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC3);
+
+ for (i = 0; i < 95; i++) {
+ urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
+ urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225_rxgain[i]);
+ }
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC4);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC5);
+
+ for (i = 0; i < 128; i++) {
+ urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]);
+ usbd_delay_ms(sc->sc_udev, 1);
+ urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80);
+ usbd_delay_ms(sc->sc_udev, 1);
+ }
+
+ for (i = 0; i < N(urtw_8225_rf_part2); i++) {
+ urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg,
+ urtw_8225_rf_part2[i].val);
+ usbd_delay_ms(sc->sc_udev, 1);
+ }
+
+ error = urtw_8225_setgain(sc, 4);
+ if (error)
+ goto fail;
+
+ for (i = 0; i < N(urtw_8225_rf_part3); i++) {
+ urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg,
+ urtw_8225_rf_part3[i].val);
+ usbd_delay_ms(sc->sc_udev, 1);
+ }
+
+ urtw_write8_m(sc, URTW_ADDR_MAGIC4, 0x0d);
+
+ error = urtw_8225_set_txpwrlvl(sc, 1);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_cck(sc, 0x10, 0x9b);
+ usbd_delay_ms(sc->sc_udev, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x26, 0x90);
+ usbd_delay_ms(sc->sc_udev, 1);
+
+ /* TX ant A, 0x0 for B */
+ error = urtw_8185_tx_antenna(sc, 0x3);
+ if (error)
+ goto fail;
+ urtw_write32_m(sc, URTW_ADDR_MAGIC5, 0x3dc00002);
+
+ error = urtw_8225_rf_set_chan(sc, 1);
+fail:
+ return (error);
+#undef N
+}
+
+static usbd_status
+urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan)
+{
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+ struct ieee80211_channel *c = ic->ic_curchan;
+ usbd_status error;
+
+ error = urtw_8225_set_txpwrlvl(sc, chan);
+ if (error)
+ goto fail;
+ urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
+ usbd_delay_ms(sc->sc_udev, 10);
+
+ urtw_write8_m(sc, URTW_SIFS, 0x22);
+
+ if (sc->sc_state == IEEE80211_S_ASSOC &&
+ ic->ic_flags & IEEE80211_F_SHSLOT)
+ urtw_write8_m(sc, URTW_SLOT, 0x9);
+ else
+ urtw_write8_m(sc, URTW_SLOT, 0x14);
+
+ if (IEEE80211_IS_CHAN_G(c)) {
+ /* for G */
+ urtw_write8_m(sc, URTW_DIFS, 0x14);
+ urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14);
+ urtw_write8_m(sc, URTW_CW_VAL, 0x73);
+ } else {
+ /* for B */
+ urtw_write8_m(sc, URTW_DIFS, 0x24);
+ urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24);
+ urtw_write8_m(sc, URTW_CW_VAL, 0xa5);
+ }
+
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens)
+{
+ usbd_status error;
+
+ if (sens < 0 || sens > 6)
+ return -1;
+
+ if (sens > 4)
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC1);
+ else
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC2);
+
+ sens = 6 - sens;
+ error = urtw_8225_setgain(sc, sens);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]);
+
+fail:
+ return (error);
+}
+
+static void
+urtw_stop(struct ifnet *ifp, int disable)
+{
+ struct urtw_softc *sc = ifp->if_softc;
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ callout_stop(&sc->sc_led_ch);
+ callout_stop(&sc->sc_watchdog_ch);
+
+ if (sc->sc_rxpipe != NULL)
+ usbd_abort_pipe(sc->sc_rxpipe);
+ if (sc->sc_txpipe_low != NULL)
+ usbd_abort_pipe(sc->sc_txpipe_low);
+ if (sc->sc_txpipe_normal != NULL)
+ usbd_abort_pipe(sc->sc_txpipe_normal);
+}
+
+static int
+urtw_isbmode(uint16_t rate)
+{
+
+ rate = urtw_rtl2rate(rate);
+
+ return ((rate <= 22 && rate != 12 && rate != 18) ||
+ rate == 44) ? (1) : (0);
+}
+
+static void
+urtw_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ int actlen, flen, len, nf, rssi;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m, *mnew;
+ struct urtw_data *data = priv;
+ struct urtw_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint8_t *desc, quality, rate;
+ usbd_status error;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_rxpipe);
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &actlen, NULL);
+ if (actlen < URTW_MIN_RXBUFSZ) {
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ /* 4 dword and 4 byte CRC */
+ len = actlen - (4 * 4);
+ desc = data->buf + len;
+ flen = ((desc[1] & 0x0f) << 8) + (desc[0] & 0xff);
+ if (flen > actlen) {
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ rate = (desc[2] & 0xf0) >> 4;
+ quality = desc[4] & 0xff;
+ /* XXX correct? */
+ rssi = (desc[6] & 0xfe) >> 1;
+ if (!urtw_isbmode(rate)) {
+ rssi = (rssi > 90) ? 90 : ((rssi < 25) ? 25 : rssi);
+ rssi = ((90 - rssi) * 100) / 65;
+ } else {
+ rssi = (rssi > 90) ? 95 : ((rssi < 30) ? 30 : rssi);
+ rssi = ((95 - rssi) * 100) / 65;
+ }
+
+ mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (mnew == NULL) {
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ m = data->m;
+ data->m = mnew;
+ data->buf = mtod(mnew, uint8_t *);
+
+ /* finalize mbuf */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = flen - 4;
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ /* XXX Are variables correct? */
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_dbm_antsignal = (int8_t)rssi;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA)
+ sc->sc_currate = (rate > 0) ? rate : sc->sc_currate;
+ ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
+ /* XXX correct? */
+ nf = (quality > 64) ? 0 : ((64 - quality) * 100) / 64;
+ /* send the frame to the 802.11 layer */
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, -nf, 0);
+ /* node is no longer needed */
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, -nf, 0);
+
+skip: /* setup a new transfer */
+ usbd_setup_xfer(xfer, sc->sc_rxpipe, data, data->buf, MCLBYTES,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, urtw_rxeof);
+ error = usbd_transfer(xfer);
+ if (error != USBD_IN_PROGRESS && error != 0)
+ device_printf(sc->sc_dev, "could not queue Rx transfer\n");
+}
+
+static int
+urtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct urtw_vap *rvp = URTW_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct urtw_softc *sc = ic->ic_ifp->if_softc;
+
+ DPRINTF(sc, URTW_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+
+ if (nstate == IEEE80211_S_INIT) {
+ rvp->newstate(vap, nstate, arg);
+ return (0);
+ } else {
+ usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
+ return (EINPROGRESS);
+ }
+}
+
+static usbd_status
+urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain)
+{
+ uint8_t *gainp;
+ usbd_status error;
+
+ /* XXX for A? */
+ gainp = urtw_8225v2_gain_bg;
+ urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]);
+ usbd_delay_ms(sc->sc_udev, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]);
+ usbd_delay_ms(sc->sc_udev, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]);
+ usbd_delay_ms(sc->sc_udev, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x21, 0x17);
+ usbd_delay_ms(sc->sc_udev, 1);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8225v2_set_txpwrlvl(struct urtw_softc *sc, int chan)
+{
+ int i;
+ uint8_t *cck_pwrtable;
+ uint8_t cck_pwrlvl_max = 15, ofdm_pwrlvl_max = 25, ofdm_pwrlvl_min = 10;
+ uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
+ uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
+ usbd_status error;
+
+ /* CCK power setting */
+ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl;
+ cck_pwrlvl += sc->sc_txpwr_cck_base;
+ cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl;
+ cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 :
+ urtw_8225v2_txpwr_cck;
+
+ for (i = 0; i < 8; i++)
+ urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_CCK,
+ urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl]);
+ usbd_delay_ms(sc->sc_udev, 1);
+
+ /* OFDM power setting */
+ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ?
+ ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min;
+ ofdm_pwrlvl += sc->sc_txpwr_ofdm_base;
+ ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
+
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_ofdm(sc, 2, 0x42);
+ urtw_8187_write_phy_ofdm(sc, 5, 0x0);
+ urtw_8187_write_phy_ofdm(sc, 6, 0x40);
+ urtw_8187_write_phy_ofdm(sc, 7, 0x0);
+ urtw_8187_write_phy_ofdm(sc, 8, 0x40);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
+ urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl]);
+ usbd_delay_ms(sc->sc_udev, 1);
+fail:
+ return (error);
+}
+
+static usbd_status
+urtw_8225v2_rf_init(struct urtw_softc *sc)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int i;
+ uint16_t data;
+ uint32_t data32;
+ usbd_status error;
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+
+ error = urtw_8225_usb_init(sc);
+ if (error)
+ goto fail;
+
+ urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008);
+ urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */
+ urtw_write16_m(sc, URTW_BRSR, 0xffff);
+ urtw_write32_m(sc, URTW_RF_PARA, 0x100044);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ urtw_write8_m(sc, URTW_CONFIG3, 0x44);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ error = urtw_8185_rf_pins_enable(sc);
+ if (error)
+ goto fail;
+
+ usbd_delay_ms(sc->sc_udev, 500);
+
+ for (i = 0; i < N(urtw_8225v2_rf_part1); i++) {
+ urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg,
+ urtw_8225v2_rf_part1[i].val);
+ }
+ usbd_delay_ms(sc->sc_udev, 50);
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1);
+
+ for (i = 0; i < 95; i++) {
+ urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
+ urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC,
+ urtw_8225v2_rxgain[i]);
+ }
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_3_MAGIC, URTW_8225_ADDR_3_DATA_MAGIC1);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_5_MAGIC, URTW_8225_ADDR_5_DATA_MAGIC1);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
+ usbd_delay_ms(sc->sc_udev, 100);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32);
+ if (error != 0)
+ goto fail;
+ if (data32 != URTW_8225_ADDR_6_DATA_MAGIC1)
+ device_printf(sc->sc_dev, "expect 0xe6!! (0x%x)\n", data32);
+ if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) {
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
+ usbd_delay_ms(sc->sc_udev, 100);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
+ usbd_delay_ms(sc->sc_udev, 50);
+ error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32);
+ if (error != 0)
+ goto fail;
+ if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2))
+ device_printf(sc->sc_dev, "RF calibration failed\n");
+ }
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC6);
+ for (i = 0; i < 128; i++) {
+ urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]);
+ urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80);
+ }
+
+ for (i = 0; i < N(urtw_8225v2_rf_part2); i++) {
+ urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg,
+ urtw_8225v2_rf_part2[i].val);
+ }
+
+ error = urtw_8225v2_setgain(sc, 4);
+ if (error)
+ goto fail;
+
+ for (i = 0; i < N(urtw_8225v2_rf_part3); i++) {
+ urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg,
+ urtw_8225v2_rf_part3[i].val);
+ }
+
+ urtw_write8_m(sc, URTW_ADDR_MAGIC4, 0x0d);
+
+ error = urtw_8225v2_set_txpwrlvl(sc, 1);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_cck(sc, 0x10, 0x9b);
+ urtw_8187_write_phy_ofdm(sc, 0x26, 0x90);
+
+ /* TX ant A, 0x0 for B */
+ error = urtw_8185_tx_antenna(sc, 0x3);
+ if (error)
+ goto fail;
+ urtw_write32_m(sc, URTW_ADDR_MAGIC5, 0x3dc00002);
+
+ error = urtw_8225_rf_set_chan(sc, 1);
+fail:
+ return (error);
+#undef N
+}
+
+static usbd_status
+urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan)
+{
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+ struct ieee80211_channel *c = ic->ic_curchan;
+ usbd_status error;
+
+ error = urtw_8225v2_set_txpwrlvl(sc, chan);
+ if (error)
+ goto fail;
+
+ urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
+ usbd_delay_ms(sc->sc_udev, 10);
+
+ urtw_write8_m(sc, URTW_SIFS, 0x22);
+
+ if(sc->sc_state == IEEE80211_S_ASSOC &&
+ ic->ic_flags & IEEE80211_F_SHSLOT)
+ urtw_write8_m(sc, URTW_SLOT, 0x9);
+ else
+ urtw_write8_m(sc, URTW_SLOT, 0x14);
+
+ if (IEEE80211_IS_CHAN_G(c)) {
+ /* for G */
+ urtw_write8_m(sc, URTW_DIFS, 0x14);
+ urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14);
+ urtw_write8_m(sc, URTW_CW_VAL, 0x73);
+ } else {
+ /* for B */
+ urtw_write8_m(sc, URTW_DIFS, 0x24);
+ urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24);
+ urtw_write8_m(sc, URTW_CW_VAL, 0xa5);
+ }
+
+fail:
+ return (error);
+}
+
+static void
+urtw_ctxtask(void *arg)
+{
+ struct urtw_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t data;
+ usbd_status error;
+
+ switch (sc->sc_ctxarg) {
+ case URTW_SET_CHANNEL:
+ /*
+ * during changing th channel we need to temporarily be disable
+ * TX.
+ */
+ urtw_read32_m(sc, URTW_TX_CONF, &data);
+ data &= ~URTW_TX_LOOPBACK_MASK;
+ urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC);
+ error = sc->sc_rf_set_chan(sc,
+ ieee80211_chan2ieee(ic, ic->ic_curchan));
+ if (error != 0)
+ goto fail;
+ usbd_delay_ms(sc->sc_udev, 10);
+ urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_NONE);
+ break;
+ default:
+ panic("unknown argument.\n");
+ }
+
+fail:
+ if (error != 0)
+ device_printf(sc->sc_dev, "could not change the channel\n");
+ return;
+}
+
+static void
+urtw_task(void *arg)
+{
+ struct urtw_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct urtw_vap *uvp = URTW_VAP(vap);
+ usbd_status error = 0;
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_RUN:
+ /* setting bssid. */
+ urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]);
+ urtw_write16_m(sc, URTW_BSSID + 4,
+ ((uint16_t *)ni->ni_bssid)[2]);
+ urtw_update_msr(sc);
+ /* XXX maybe the below would be incorrect. */
+ urtw_write16_m(sc, URTW_ATIM_WND, 2);
+ urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100);
+ urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64);
+ urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100);
+ error = urtw_led_ctl(sc, URTW_LED_CTL_LINK);
+ if (error != 0)
+ device_printf(sc->sc_dev,
+ "could not control LED (%d)\n", error);
+ break;
+ default:
+ break;
+ }
+
+fail:
+ if (error != 0)
+ printf("error duing processing RUN state.");
+
+ IEEE80211_LOCK(ic);
+ uvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+urtw_watchdog(void *arg)
+{
+ struct urtw_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if (sc->sc_txtimer > 0) {
+ if (--sc->sc_txtimer == 0) {
+ device_printf(sc->sc_dev, "device timeout\n");
+ ifp->if_oerrors++;
+ return;
+ }
+ callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
+ }
+}
+
+static device_method_t urtw_methods[] = {
+ DEVMETHOD(device_probe, urtw_match),
+ DEVMETHOD(device_attach, urtw_attach),
+ DEVMETHOD(device_detach, urtw_detach),
+ { 0, 0 }
+};
+static driver_t urtw_driver = {
+ "urtw",
+ urtw_methods,
+ sizeof(struct urtw_softc)
+};
+static devclass_t urtw_devclass;
+
+DRIVER_MODULE(urtw, uhub, urtw_driver, urtw_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(urtw, wlan, 1, 1, 1);
+MODULE_DEPEND(urtw, usb, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/if_urtwreg.h b/sys/legacy/dev/usb/if_urtwreg.h
new file mode 100644
index 0000000..7a9baa3
--- /dev/null
+++ b/sys/legacy/dev/usb/if_urtwreg.h
@@ -0,0 +1,256 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define URTW_CONFIG_NO 1
+#define URTW_IFACE_INDEX 0
+
+/* for 8187 */
+#define URTW_MAC0 0x0000 /* 1 byte */
+#define URTW_MAC1 0x0001 /* 1 byte */
+#define URTW_MAC2 0x0002 /* 1 byte */
+#define URTW_MAC3 0x0003 /* 1 byte */
+#define URTW_MAC4 0x0004 /* 1 byte */
+#define URTW_MAC5 0x0005 /* 1 byte */
+#define URTW_BRSR 0x002c /* 2 byte */
+#define URTW_BRSR_MBR_8185 (0x0fff)
+#define URTW_BSSID 0x002e /* 6 byte */
+#define URTW_RESP_RATE 0x0034 /* 1 byte */
+#define URTW_RESP_MAX_RATE_SHIFT (4)
+#define URTW_RESP_MIN_RATE_SHIFT (0)
+#define URTW_EIFS 0x0035 /* 1 byte */
+#define URTW_INTR_MASK 0x003c /* 2 byte */
+#define URTW_CMD 0x0037 /* 1 byte */
+#define URTW_CMD_TX_ENABLE (0x4)
+#define URTW_CMD_RX_ENABLE (0x8)
+#define URTW_CMD_RST (0x10)
+#define URTW_TX_CONF 0x0040 /* 4 byte */
+#define URTW_TX_LOOPBACK_SHIFT (17)
+#define URTW_TX_LOOPBACK_NONE (0 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_MAC (1 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_BASEBAND (2 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_CONTINUE (3 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_MASK (0x60000)
+#define URTW_TX_DPRETRY_MASK (0xff00)
+#define URTW_TX_RTSRETRY_MASK (0xff)
+#define URTW_TX_DPRETRY_SHIFT (0)
+#define URTW_TX_RTSRETRY_SHIFT (8)
+#define URTW_TX_NOCRC (0x10000)
+#define URTW_TX_MXDMA_MASK (0xe00000)
+#define URTW_TX_MXDMA_1024 (6 << URTW_TX_MXDMA_SHIFT)
+#define URTW_TX_MXDMA_2048 (7 << URTW_TX_MXDMA_SHIFT)
+#define URTW_TX_MXDMA_SHIFT (21)
+#define URTW_TX_CWMIN (1 << 31)
+#define URTW_TX_DISCW (1 << 20)
+#define URTW_TX_SWPLCPLEN (1 << 24)
+#define URTW_TX_NOICV (0x80000)
+#define URTW_RX 0x0044 /* 4 byte */
+#define URTW_RX_9356SEL (1 << 6)
+#define URTW_RX_FILTER_MASK \
+ (URTW_RX_FILTER_ALLMAC | URTW_RX_FILTER_NICMAC | URTW_RX_FILTER_MCAST | \
+ URTW_RX_FILTER_BCAST | URTW_RX_FILTER_CRCERR | URTW_RX_FILTER_ICVERR | \
+ URTW_RX_FILTER_DATA | URTW_RX_FILTER_CTL | URTW_RX_FILTER_MNG | \
+ (1 << 21) | \
+ URTW_RX_FILTER_PWR | URTW_RX_CHECK_BSSID)
+#define URTW_RX_FILTER_ALLMAC (0x00000001)
+#define URTW_RX_FILTER_NICMAC (0x00000002)
+#define URTW_RX_FILTER_MCAST (0x00000004)
+#define URTW_RX_FILTER_BCAST (0x00000008)
+#define URTW_RX_FILTER_CRCERR (0x00000020)
+#define URTW_RX_FILTER_ICVERR (0x00001000)
+#define URTW_RX_FILTER_DATA (0x00040000)
+#define URTW_RX_FILTER_CTL (0x00080000)
+#define URTW_RX_FILTER_MNG (0x00100000)
+#define URTW_RX_FILTER_PWR (0x00400000)
+#define URTW_RX_CHECK_BSSID (0x00800000)
+#define URTW_RX_FIFO_THRESHOLD_MASK ((1 << 13) | (1 << 14) | (1 << 15))
+#define URTW_RX_FIFO_THRESHOLD_SHIFT (13)
+#define URTW_RX_FIFO_THRESHOLD_128 (3)
+#define URTW_RX_FIFO_THRESHOLD_256 (4)
+#define URTW_RX_FIFO_THRESHOLD_512 (5)
+#define URTW_RX_FIFO_THRESHOLD_1024 (6)
+#define URTW_RX_FIFO_THRESHOLD_NONE (7 << URTW_RX_FIFO_THRESHOLD_SHIFT)
+#define URTW_RX_AUTORESETPHY (1 << URTW_RX_AUTORESETPHY_SHIFT)
+#define URTW_RX_AUTORESETPHY_SHIFT (28)
+#define URTW_MAX_RX_DMA_MASK ((1<<8) | (1<<9) | (1<<10))
+#define URTW_MAX_RX_DMA_2048 (7 << URTW_MAX_RX_DMA_SHIFT)
+#define URTW_MAX_RX_DMA_1024 (6)
+#define URTW_MAX_RX_DMA_SHIFT (10)
+#define URTW_RCR_ONLYERLPKT (1 << 31)
+#define URTW_INT_TIMEOUT 0x0048 /* 4 byte */
+#define URTW_EPROM_CMD 0x0050 /* 1 byte */
+#define URTW_EPROM_CMD_NORMAL (0x0)
+#define URTW_EPROM_CMD_NORMAL_MODE \
+ (URTW_EPROM_CMD_NORMAL << URTW_EPROM_CMD_SHIFT)
+#define URTW_EPROM_CMD_LOAD (0x1)
+#define URTW_EPROM_CMD_PROGRAM (0x2)
+#define URTW_EPROM_CMD_PROGRAM_MODE \
+ (URTW_EPROM_CMD_PROGRAM << URTW_EPROM_CMD_SHIFT)
+#define URTW_EPROM_CMD_CONFIG (0x3)
+#define URTW_EPROM_CMD_SHIFT (6)
+#define URTW_EPROM_CMD_MASK ((1 << 7) | (1 << 6))
+#define URTW_EPROM_READBIT (0x1)
+#define URTW_EPROM_WRITEBIT (0x2)
+#define URTW_EPROM_CK (0x4)
+#define URTW_EPROM_CS (0x8)
+#define URTW_CONFIG2 0x0053
+#define URTW_ANAPARAM 0x0054 /* 4 byte */
+#define URTW_8225_ANAPARAM_ON (0xa0000a59)
+#define URTW_MSR 0x0058 /* 1 byte */
+#define URTW_MSR_LINK_MASK ((1 << 2) | (1 << 3))
+#define URTW_MSR_LINK_SHIFT (2)
+#define URTW_MSR_LINK_NONE (0 << URTW_MSR_LINK_SHIFT)
+#define URTW_MSR_LINK_ADHOC (1 << URTW_MSR_LINK_SHIFT)
+#define URTW_MSR_LINK_STA (2 << URTW_MSR_LINK_SHIFT)
+#define URTW_MSR_LINK_HOSTAP (3 << URTW_MSR_LINK_SHIFT)
+#define URTW_CONFIG3 0x0059 /* 1 byte */
+#define URTW_CONFIG3_ANAPARAM_WRITE (0x40)
+#define URTW_CONFIG3_ANAPARAM_W_SHIFT (6)
+#define URTW_ADDR_MAGIC4 0x005b /* 1 byte */
+#define URTW_PSR 0x005e /* 1 byte */
+#define URTW_ANAPARAM2 0x0060 /* 4 byte */
+#define URTW_8225_ANAPARAM2_ON (0x860c7312)
+#define URTW_BEACON_INTERVAL 0x0070 /* 2 byte */
+#define URTW_ATIM_WND 0x0072 /* 2 byte */
+#define URTW_BEACON_INTERVAL_TIME 0x0074 /* 2 byte */
+#define URTW_ATIM_TR_ITV 0x0076 /* 2 byte */
+#define URTW_PHY_MAGIC1 0x007c /* 1 byte */
+#define URTW_PHY_MAGIC2 0x007d /* 1 byte */
+#define URTW_PHY_MAGIC3 0x007e /* 1 byte */
+#define URTW_PHY_MAGIC4 0x007f /* 1 byte */
+#define URTW_RF_PINS_OUTPUT 0x0080 /* 2 byte */
+#define URTW_RF_PINS_OUTPUT_MAGIC1 (0x3a0)
+#define URTW_BB_HOST_BANG_CLK (1 << 1)
+#define URTW_BB_HOST_BANG_EN (1 << 2)
+#define URTW_BB_HOST_BANG_RW (1 << 3)
+#define URTW_RF_PINS_ENABLE 0x0082 /* 2 byte */
+#define URTW_RF_PINS_SELECT 0x0084 /* 2 byte */
+#define URTW_ADDR_MAGIC1 0x0085 /* broken? */
+#define URTW_RF_PINS_INPUT 0x0086 /* 2 byte */
+#define URTW_RF_PINS_MAGIC1 (0xfff3)
+#define URTW_RF_PINS_MAGIC2 (0xfff0)
+#define URTW_RF_PINS_MAGIC3 (0x0007)
+#define URTW_RF_PINS_MAGIC4 (0xf)
+#define URTW_RF_PINS_MAGIC5 (0x0080)
+#define URTW_RF_PARA 0x0088 /* 4 byte */
+#define URTW_RF_TIMING 0x008c /* 4 byte */
+#define URTW_GP_ENABLE 0x0090 /* 1 byte */
+#define URTW_GP_ENABLE_DATA_MAGIC1 (0x1)
+#define URTW_GPIO 0x0091 /* 1 byte */
+#define URTW_GPIO_DATA_MAGIC1 (0x1)
+#define URTW_ADDR_MAGIC5 0x0094 /* 4 byte */
+#define URTW_TX_AGC_CTL 0x009c /* 1 byte */
+#define URTW_TX_AGC_CTL_PERPACKET_GAIN (0x1)
+#define URTW_TX_AGC_CTL_PERPACKET_ANTSEL (0x2)
+#define URTW_TX_AGC_CTL_FEEDBACK_ANT (0x4)
+#define URTW_TX_GAIN_CCK 0x009d /* 1 byte */
+#define URTW_TX_GAIN_OFDM 0x009e /* 1 byte */
+#define URTW_TX_ANTENNA 0x009f /* 1 byte */
+#define URTW_WPA_CONFIG 0x00b0 /* 1 byte */
+#define URTW_SIFS 0x00b4 /* 1 byte */
+#define URTW_DIFS 0x00b5 /* 1 byte */
+#define URTW_SLOT 0x00b6 /* 1 byte */
+#define URTW_CW_CONF 0x00bc /* 1 byte */
+#define URTW_CW_CONF_PERPACKET_RETRY (0x2)
+#define URTW_CW_CONF_PERPACKET_CW (0x1)
+#define URTW_CW_VAL 0x00bd /* 1 byte */
+#define URTW_RATE_FALLBACK 0x00be /* 1 byte */
+#define URTW_TALLY_SEL 0x00fc /* 1 byte */
+#define URTW_ADDR_MAGIC2 0x00fe /* 2 byte */
+#define URTW_ADDR_MAGIC3 0x00ff /* 1 byte */
+
+/* for 8225 */
+#define URTW_8225_ADDR_0_MAGIC 0x0
+#define URTW_8225_ADDR_0_DATA_MAGIC1 (0x1b7)
+#define URTW_8225_ADDR_0_DATA_MAGIC2 (0x0b7)
+#define URTW_8225_ADDR_0_DATA_MAGIC3 (0x127)
+#define URTW_8225_ADDR_0_DATA_MAGIC4 (0x027)
+#define URTW_8225_ADDR_0_DATA_MAGIC5 (0x22f)
+#define URTW_8225_ADDR_0_DATA_MAGIC6 (0x2bf)
+#define URTW_8225_ADDR_1_MAGIC 0x1
+#define URTW_8225_ADDR_2_MAGIC 0x2
+#define URTW_8225_ADDR_2_DATA_MAGIC1 (0xc4d)
+#define URTW_8225_ADDR_2_DATA_MAGIC2 (0x44d)
+#define URTW_8225_ADDR_3_MAGIC 0x3
+#define URTW_8225_ADDR_3_DATA_MAGIC1 (0x2)
+#define URTW_8225_ADDR_5_MAGIC 0x5
+#define URTW_8225_ADDR_5_DATA_MAGIC1 (0x4)
+#define URTW_8225_ADDR_6_MAGIC 0x6
+#define URTW_8225_ADDR_6_DATA_MAGIC1 (0xe6)
+#define URTW_8225_ADDR_6_DATA_MAGIC2 (0x80)
+#define URTW_8225_ADDR_7_MAGIC 0x7
+#define URTW_8225_ADDR_8_MAGIC 0x8
+#define URTW_8225_ADDR_8_DATA_MAGIC1 (0x588)
+#define URTW_8225_ADDR_9_MAGIC 0x9
+#define URTW_8225_ADDR_9_DATA_MAGIC1 (0x700)
+#define URTW_8225_ADDR_C_MAGIC 0xc
+#define URTW_8225_ADDR_C_DATA_MAGIC1 (0x850)
+#define URTW_8225_ADDR_C_DATA_MAGIC2 (0x050)
+
+/* for EEPROM */
+#define URTW_EPROM_TXPW_BASE 0x05
+#define URTW_EPROM_RFCHIPID 0x06
+#define URTW_EPROM_RFCHIPID_RTL8225U (5)
+#define URTW_EPROM_MACADDR 0x07
+#define URTW_EPROM_TXPW0 0x16
+#define URTW_EPROM_TXPW2 0x1b
+#define URTW_EPROM_TXPW1 0x3d
+#define URTW_EPROM_SWREV 0x3f
+#define URTW_EPROM_CID_MASK (0xff)
+#define URTW_EPROM_CID_RSVD0 (0x00)
+#define URTW_EPROM_CID_RSVD1 (0xff)
+#define URTW_EPROM_CID_ALPHA0 (0x01)
+#define URTW_EPROM_CID_SERCOMM_PS (0x02)
+#define URTW_EPROM_CID_HW_LED (0x03)
+
+/* LED */
+#define URTW_CID_DEFAULT 0
+#define URTW_CID_8187_ALPHA0 1
+#define URTW_CID_8187_SERCOMM_PS 2
+#define URTW_CID_8187_HW_LED 3
+#define URTW_SW_LED_MODE0 0
+#define URTW_SW_LED_MODE1 1
+#define URTW_SW_LED_MODE2 2
+#define URTW_SW_LED_MODE3 3
+#define URTW_HW_LED 4
+#define URTW_LED_CTL_POWER_ON 0
+#define URTW_LED_CTL_LINK 2
+#define URTW_LED_CTL_TX 4
+#define URTW_LED_PIN_GPIO0 0
+#define URTW_LED_PIN_LED0 1
+#define URTW_LED_PIN_LED1 2
+#define URTW_LED_UNKNOWN 0
+#define URTW_LED_ON 1
+#define URTW_LED_OFF 2
+#define URTW_LED_BLINK_NORMAL 3
+#define URTW_LED_BLINK_SLOWLY 4
+#define URTW_LED_POWER_ON_BLINK 5
+#define URTW_LED_SCAN_BLINK 6
+#define URTW_LED_NO_LINK_BLINK 7
+#define URTW_LED_BLINK_CM3 8
+
+/* for extra area */
+#define URTW_EPROM_DISABLE 0
+#define URTW_EPROM_ENABLE 1
+#define URTW_EPROM_DELAY 10
+#define URTW_8187_GETREGS_REQ 5
+#define URTW_8187_SETREGS_REQ 5
+#define URTW_8225_RF_MAX_SENS 6
+#define URTW_8225_RF_DEF_SENS 4
+#define URTW_DEFAULT_RTS_RETRY 7
+#define URTW_DEFAULT_TX_RETRY 7
+#define URTW_DEFAULT_RTS_THRESHOLD 2342U
diff --git a/sys/legacy/dev/usb/if_urtwvar.h b/sys/legacy/dev/usb/if_urtwvar.h
new file mode 100644
index 0000000..77c09ef
--- /dev/null
+++ b/sys/legacy/dev/usb/if_urtwvar.h
@@ -0,0 +1,151 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* XXX no definition at net80211? */
+#define URTW_MAX_CHANNELS 15
+
+struct urtw_data {
+ struct urtw_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct mbuf *m;
+ struct ieee80211_node *ni; /* NB: tx only */
+};
+
+/* XXX not correct.. */
+#define URTW_MIN_RXBUFSZ \
+ (sizeof(struct ieee80211_frame_min))
+
+#define URTW_RX_DATA_LIST_COUNT 1
+#define URTW_TX_DATA_LIST_COUNT 16
+#define URTW_RX_MAXSIZE 0x9c4
+#define URTW_TX_MAXSIZE 0x9c4
+
+struct urtw_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_dbm_antsignal;
+} __packed;
+
+#define URTW_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL))
+
+struct urtw_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed;
+
+#define URTW_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct urtw_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define URTW_VAP(vap) ((struct urtw_vap *)(vap))
+
+struct urtw_softc {
+ struct ifnet *sc_ifp;
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+ struct mtx sc_mtx;
+
+ int sc_debug;
+ int sc_if_flags;
+ int sc_flags;
+#define URTW_INIT_ONCE (1 << 1)
+ struct usb_task sc_task;
+ struct usb_task sc_ctxtask;
+ int sc_ctxarg;
+#define URTW_SET_CHANNEL 1
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int (*sc_newstate)(struct ieee80211com *,
+ enum ieee80211_state, int);
+
+ int sc_epromtype;
+#define URTW_EEPROM_93C46 0
+#define URTW_EEPROM_93C56 1
+ uint8_t sc_crcmon;
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+
+ /* for RF */
+ usbd_status (*sc_rf_init)(struct urtw_softc *);
+ usbd_status (*sc_rf_set_chan)(struct urtw_softc *,
+ int);
+ usbd_status (*sc_rf_set_sens)(struct urtw_softc *,
+ int);
+ uint8_t sc_rfchip;
+ uint32_t sc_max_sens;
+ uint32_t sc_sens;
+ /* for LED */
+ struct callout sc_led_ch;
+ struct usb_task sc_ledtask;
+ uint8_t sc_psr;
+ uint8_t sc_strategy;
+#define URTW_LED_GPIO 1
+ uint8_t sc_gpio_ledon;
+ uint8_t sc_gpio_ledinprogress;
+ uint8_t sc_gpio_ledstate;
+ uint8_t sc_gpio_ledpin;
+ uint8_t sc_gpio_blinktime;
+ uint8_t sc_gpio_blinkstate;
+ /* RX/TX */
+ usbd_pipe_handle sc_rxpipe;
+ usbd_pipe_handle sc_txpipe_low;
+ usbd_pipe_handle sc_txpipe_normal;
+#define URTW_PRIORITY_LOW 0
+#define URTW_PRIORITY_NORMAL 1
+#define URTW_DATA_TIMEOUT 10000 /* 10 sec */
+ struct urtw_data sc_rxdata[URTW_RX_DATA_LIST_COUNT];
+ struct urtw_data sc_txdata[URTW_TX_DATA_LIST_COUNT];
+ uint32_t sc_tx_low_queued;
+ uint32_t sc_tx_normal_queued;
+ uint32_t sc_txidx;
+ uint8_t sc_rts_retry;
+ uint8_t sc_tx_retry;
+ uint8_t sc_preamble_mode;
+#define URTW_PREAMBLE_MODE_SHORT 1
+#define URTW_PREAMBLE_MODE_LONG 2
+ struct callout sc_watchdog_ch;
+ int sc_txtimer;
+ int sc_currate;
+ /* TX power */
+ uint8_t sc_txpwr_cck[URTW_MAX_CHANNELS];
+ uint8_t sc_txpwr_cck_base;
+ uint8_t sc_txpwr_ofdm[URTW_MAX_CHANNELS];
+ uint8_t sc_txpwr_ofdm_base;
+
+ struct urtw_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+ struct urtw_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#define URTW_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define URTW_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define URTW_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
diff --git a/sys/legacy/dev/usb/if_zyd.c b/sys/legacy/dev/usb/if_zyd.c
new file mode 100644
index 0000000..3a4abce
--- /dev/null
+++ b/sys/legacy/dev/usb/if_zyd.c
@@ -0,0 +1,3126 @@
+/* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */
+/* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * ZyDAS ZD1211/ZD1211B USB WLAN driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_regdomain.h>
+
+#include <net/bpf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_ethersubr.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/if_zydreg.h>
+#include <dev/usb/if_zydfw.h>
+
+#ifdef ZYD_DEBUG
+SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW, 0, "ZyDAS zd1211/zd1211b");
+int zyd_debug = 0;
+SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0,
+ "control debugging printfs");
+TUNABLE_INT("hw.usb.zyd.debug", &zyd_debug);
+enum {
+ ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */
+ ZYD_DEBUG_RESET = 0x00000004, /* reset processing */
+ ZYD_DEBUG_INIT = 0x00000008, /* device init */
+ ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */
+ ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */
+ ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */
+ ZYD_DEBUG_STAT = 0x00000080, /* statistic */
+ ZYD_DEBUG_FW = 0x00000100, /* firmware */
+ ZYD_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (sc->sc_debug & (m)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+
+static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY;
+static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB;
+
+/* various supported device vendors/products */
+#define ZYD_ZD1211_DEV(v, p) \
+ { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, ZYD_ZD1211 }
+#define ZYD_ZD1211B_DEV(v, p) \
+ { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, ZYD_ZD1211B }
+static const struct zyd_type {
+ struct usb_devno dev;
+ uint8_t rev;
+#define ZYD_ZD1211 0
+#define ZYD_ZD1211B 1
+} zyd_devs[] = {
+ ZYD_ZD1211_DEV(3COM2, 3CRUSB10075),
+ ZYD_ZD1211_DEV(ABOCOM, WL54),
+ ZYD_ZD1211_DEV(ASUS, WL159G),
+ ZYD_ZD1211_DEV(CYBERTAN, TG54USB),
+ ZYD_ZD1211_DEV(DRAYTEK, VIGOR550),
+ ZYD_ZD1211_DEV(PLANEX2, GWUS54GD),
+ ZYD_ZD1211_DEV(PLANEX2, GWUS54GZL),
+ ZYD_ZD1211_DEV(PLANEX3, GWUS54GZ),
+ ZYD_ZD1211_DEV(PLANEX3, GWUS54MINI),
+ ZYD_ZD1211_DEV(SAGEM, XG760A),
+ ZYD_ZD1211_DEV(SENAO, NUB8301),
+ ZYD_ZD1211_DEV(SITECOMEU, WL113),
+ ZYD_ZD1211_DEV(SWEEX, ZD1211),
+ ZYD_ZD1211_DEV(TEKRAM, QUICKWLAN),
+ ZYD_ZD1211_DEV(TEKRAM, ZD1211_1),
+ ZYD_ZD1211_DEV(TEKRAM, ZD1211_2),
+ ZYD_ZD1211_DEV(TWINMOS, G240),
+ ZYD_ZD1211_DEV(UMEDIA, ALL0298V2),
+ ZYD_ZD1211_DEV(UMEDIA, TEW429UB_A),
+ ZYD_ZD1211_DEV(UMEDIA, TEW429UB),
+ ZYD_ZD1211_DEV(WISTRONNEWEB, UR055G),
+ ZYD_ZD1211_DEV(ZCOM, ZD1211),
+ ZYD_ZD1211_DEV(ZYDAS, ZD1211),
+ ZYD_ZD1211_DEV(ZYXEL, AG225H),
+ ZYD_ZD1211_DEV(ZYXEL, ZYAIRG220),
+ ZYD_ZD1211_DEV(ZYXEL, G200V2),
+ ZYD_ZD1211_DEV(ZYXEL, G202),
+
+ ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG),
+ ZYD_ZD1211B_DEV(ACCTON, ZD1211B),
+ ZYD_ZD1211B_DEV(ASUS, A9T_WIFI),
+ ZYD_ZD1211B_DEV(BELKIN, F5D7050_V4000),
+ ZYD_ZD1211B_DEV(BELKIN, ZD1211B),
+ ZYD_ZD1211B_DEV(CISCOLINKSYS, WUSBF54G),
+ ZYD_ZD1211B_DEV(FIBERLINE, WL430U),
+ ZYD_ZD1211B_DEV(MELCO, KG54L),
+ ZYD_ZD1211B_DEV(PHILIPS, SNU5600),
+ ZYD_ZD1211B_DEV(PLANEX2, GW_US54GXS),
+ ZYD_ZD1211B_DEV(SAGEM, XG76NA),
+ ZYD_ZD1211B_DEV(SITECOMEU, ZD1211B),
+ ZYD_ZD1211B_DEV(UMEDIA, TEW429UBC1),
+#if 0 /* Shall we needs? */
+ ZYD_ZD1211B_DEV(UNKNOWN1, ZD1211B_1),
+ ZYD_ZD1211B_DEV(UNKNOWN1, ZD1211B_2),
+ ZYD_ZD1211B_DEV(UNKNOWN2, ZD1211B),
+ ZYD_ZD1211B_DEV(UNKNOWN3, ZD1211B),
+#endif
+ ZYD_ZD1211B_DEV(USR, USR5423),
+ ZYD_ZD1211B_DEV(VTECH, ZD1211B),
+ ZYD_ZD1211B_DEV(ZCOM, ZD1211B),
+ ZYD_ZD1211B_DEV(ZYDAS, ZD1211B),
+ ZYD_ZD1211B_DEV(ZYXEL, M202),
+ ZYD_ZD1211B_DEV(ZYXEL, G220V2),
+};
+#define zyd_lookup(v, p) \
+ ((const struct zyd_type *)usb_lookup(zyd_devs, v, p))
+#define zyd_read16_m(sc, val, data) do { \
+ error = zyd_read16(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_write16_m(sc, val, data) do { \
+ error = zyd_write16(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_read32_m(sc, val, data) do { \
+ error = zyd_read32(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_write32_m(sc, val, data) do { \
+ error = zyd_write32(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+
+static device_probe_t zyd_match;
+static device_attach_t zyd_attach;
+static device_detach_t zyd_detach;
+
+static struct ieee80211vap *zyd_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void zyd_vap_delete(struct ieee80211vap *);
+static int zyd_open_pipes(struct zyd_softc *);
+static void zyd_close_pipes(struct zyd_softc *);
+static int zyd_alloc_tx_list(struct zyd_softc *);
+static void zyd_free_tx_list(struct zyd_softc *);
+static int zyd_alloc_rx_list(struct zyd_softc *);
+static void zyd_free_rx_list(struct zyd_softc *);
+static struct ieee80211_node *zyd_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void zyd_task(void *);
+static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int,
+ void *, int, u_int);
+static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *);
+static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *);
+static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t);
+static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t);
+static int zyd_rfwrite(struct zyd_softc *, uint32_t);
+static int zyd_lock_phy(struct zyd_softc *);
+static int zyd_unlock_phy(struct zyd_softc *);
+static int zyd_rf_attach(struct zyd_softc *, uint8_t);
+static const char *zyd_rf_name(uint8_t);
+static int zyd_hw_init(struct zyd_softc *);
+static int zyd_read_pod(struct zyd_softc *);
+static int zyd_read_eeprom(struct zyd_softc *);
+static int zyd_get_macaddr(struct zyd_softc *);
+static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *);
+static int zyd_set_bssid(struct zyd_softc *, const uint8_t *);
+static int zyd_switch_radio(struct zyd_softc *, int);
+static int zyd_set_led(struct zyd_softc *, int, int);
+static void zyd_set_multi(void *);
+static void zyd_update_mcast(struct ifnet *);
+static int zyd_set_rxfilter(struct zyd_softc *);
+static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *);
+static int zyd_set_beacon_interval(struct zyd_softc *, int);
+static void zyd_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void zyd_rx_data(struct zyd_softc *, const uint8_t *, uint16_t);
+static void zyd_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void zyd_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static int zyd_tx_mgt(struct zyd_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int zyd_tx_data(struct zyd_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static void zyd_start(struct ifnet *);
+static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void zyd_watchdog(void *);
+static int zyd_ioctl(struct ifnet *, u_long, caddr_t);
+static void zyd_init_locked(struct zyd_softc *);
+static void zyd_init(void *);
+static void zyd_stop(struct zyd_softc *, int);
+static int zyd_loadfirmware(struct zyd_softc *);
+static void zyd_newassoc(struct ieee80211_node *, int);
+static void zyd_scantask(void *);
+static void zyd_scan_start(struct ieee80211com *);
+static void zyd_scan_end(struct ieee80211com *);
+static void zyd_set_channel(struct ieee80211com *);
+static void zyd_wakeup(struct zyd_softc *);
+static int zyd_rfmd_init(struct zyd_rf *);
+static int zyd_rfmd_switch_radio(struct zyd_rf *, int);
+static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2230_init(struct zyd_rf *);
+static int zyd_al2230_switch_radio(struct zyd_rf *, int);
+static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t);
+static int zyd_al2230_init_b(struct zyd_rf *);
+static int zyd_al7230B_init(struct zyd_rf *);
+static int zyd_al7230B_switch_radio(struct zyd_rf *, int);
+static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2210_init(struct zyd_rf *);
+static int zyd_al2210_switch_radio(struct zyd_rf *, int);
+static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_gct_init(struct zyd_rf *);
+static int zyd_gct_switch_radio(struct zyd_rf *, int);
+static int zyd_gct_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_maxim_init(struct zyd_rf *);
+static int zyd_maxim_switch_radio(struct zyd_rf *, int);
+static int zyd_maxim_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_maxim2_init(struct zyd_rf *);
+static int zyd_maxim2_switch_radio(struct zyd_rf *, int);
+static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t);
+
+static int
+zyd_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+
+ return (zyd_lookup(uaa->vendor, uaa->product) != NULL) ?
+ (UMATCH_VENDOR_PRODUCT) : (UMATCH_NONE);
+}
+
+static int
+zyd_attach(device_t dev)
+{
+ int error = ENXIO;
+ struct ieee80211com *ic;
+ struct ifnet *ifp;
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct zyd_softc *sc = device_get_softc(dev);
+ usb_device_descriptor_t* ddesc;
+ uint8_t bands;
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+ sc->sc_macrev = zyd_lookup(uaa->vendor, uaa->product)->rev;
+#ifdef ZYD_DEBUG
+ sc->sc_debug = zyd_debug;
+#endif
+
+ ddesc = usbd_get_device_descriptor(sc->sc_udev);
+ if (UGETW(ddesc->bcdDevice) < 0x4330) {
+ device_printf(dev, "device version mismatch: 0x%x "
+ "(only >= 43.30 supported)\n",
+ UGETW(ddesc->bcdDevice));
+ return (ENXIO);
+ }
+
+ if ((error = zyd_get_macaddr(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_txmtx, device_get_nameunit(sc->sc_dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+ usb_init_task(&sc->sc_mcasttask, zyd_set_multi, sc);
+ usb_init_task(&sc->sc_scantask, zyd_scantask, sc);
+ usb_init_task(&sc->sc_task, zyd_task, sc);
+ callout_init(&sc->sc_watchdog_ch, 0);
+ STAILQ_INIT(&sc->sc_rqh);
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(dev, "can not if_alloc()\n");
+ error = ENXIO;
+ goto fail0;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "zyd", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
+ IFF_NEEDSGIANT; /* USB stack is still under Giant lock */
+ ifp->if_init = zyd_init;
+ ifp->if_ioctl = zyd_ioctl;
+ ifp->if_start = zyd_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic = ifp->if_l2com;
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA;
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid);
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_newassoc = zyd_newassoc;
+ ic->ic_raw_xmit = zyd_raw_xmit;
+ ic->ic_node_alloc = zyd_node_alloc;
+ ic->ic_scan_start = zyd_scan_start;
+ ic->ic_scan_end = zyd_scan_end;
+ ic->ic_set_channel = zyd_set_channel;
+
+ ic->ic_vap_create = zyd_vap_create;
+ ic->ic_vap_delete = zyd_vap_delete;
+ ic->ic_update_mcast = zyd_update_mcast;
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap));
+ sc->sc_rxtap_len = sizeof(sc->sc_rxtap);
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT);
+ sc->sc_txtap_len = sizeof(sc->sc_txtap);
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
+
+ return (0);
+
+fail0: mtx_destroy(&sc->sc_txmtx);
+ return (error);
+}
+
+static int
+zyd_detach(device_t dev)
+{
+ struct zyd_softc *sc = device_get_softc(dev);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ if (!device_is_attached(dev))
+ return (0);
+
+ /* set a flag to indicate we're detaching. */
+ sc->sc_flags |= ZYD_FLAG_DETACHING;
+
+ zyd_stop(sc, 1);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
+ zyd_wakeup(sc);
+ zyd_close_pipes(sc);
+
+ if_free(ifp);
+ mtx_destroy(&sc->sc_txmtx);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+zyd_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct zyd_vap *zvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return (NULL);
+ zvp = (struct zyd_vap *) malloc(sizeof(struct zyd_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (zvp == NULL)
+ return (NULL);
+ vap = &zvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ zvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = zyd_newstate;
+
+ ieee80211_amrr_init(&zvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return (vap);
+}
+
+static void
+zyd_vap_delete(struct ieee80211vap *vap)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+
+ ieee80211_amrr_cleanup(&zvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(zvp, M_80211_VAP);
+}
+
+static int
+zyd_open_pipes(struct zyd_softc *sc)
+{
+ usb_endpoint_descriptor_t *edesc;
+ int isize;
+ usbd_status error;
+
+ /* interrupt in */
+ edesc = usbd_get_endpoint_descriptor(sc->sc_iface, 0x83);
+ if (edesc == NULL)
+ return (EINVAL);
+
+ isize = UGETW(edesc->wMaxPacketSize);
+ if (isize == 0) /* should not happen */
+ return (EINVAL);
+
+ sc->sc_ibuf = malloc(isize, M_USBDEV, M_NOWAIT);
+ if (sc->sc_ibuf == NULL)
+ return (ENOMEM);
+
+ error = usbd_open_pipe_intr(sc->sc_iface, 0x83, USBD_SHORT_XFER_OK,
+ &sc->sc_ep[ZYD_ENDPT_IIN], sc, sc->sc_ibuf, isize, zyd_intr,
+ USBD_DEFAULT_INTERVAL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "open rx intr pipe failed: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ /* interrupt out (not necessarily an interrupt pipe) */
+ error = usbd_open_pipe(sc->sc_iface, 0x04, USBD_EXCLUSIVE_USE,
+ &sc->sc_ep[ZYD_ENDPT_IOUT]);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "open tx intr pipe failed: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ /* bulk in */
+ error = usbd_open_pipe(sc->sc_iface, 0x82, USBD_EXCLUSIVE_USE,
+ &sc->sc_ep[ZYD_ENDPT_BIN]);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "open rx pipe failed: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ /* bulk out */
+ error = usbd_open_pipe(sc->sc_iface, 0x01, USBD_EXCLUSIVE_USE,
+ &sc->sc_ep[ZYD_ENDPT_BOUT]);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "open tx pipe failed: %s\n",
+ usbd_errstr(error));
+ goto fail;
+ }
+
+ return (0);
+
+fail: zyd_close_pipes(sc);
+ return (ENXIO);
+}
+
+static void
+zyd_close_pipes(struct zyd_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < ZYD_ENDPT_CNT; i++) {
+ if (sc->sc_ep[i] != NULL) {
+ usbd_abort_pipe(sc->sc_ep[i]);
+ usbd_close_pipe(sc->sc_ep[i]);
+ sc->sc_ep[i] = NULL;
+ }
+ }
+ if (sc->sc_ibuf != NULL) {
+ free(sc->sc_ibuf, M_USBDEV);
+ sc->sc_ibuf = NULL;
+ }
+}
+
+static int
+zyd_alloc_tx_list(struct zyd_softc *sc)
+{
+ int i, error;
+
+ sc->sc_txqueued = 0;
+
+ for (i = 0; i < ZYD_TX_LIST_CNT; i++) {
+ struct zyd_tx_data *data = &sc->sc_txdata[i];
+
+ data->sc = sc; /* backpointer for callbacks */
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate tx xfer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ data->buf = usbd_alloc_buffer(data->xfer, ZYD_MAX_TXBUFSZ);
+ if (data->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate tx buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ /* clear Tx descriptor */
+ bzero(data->buf, sizeof(struct zyd_tx_desc));
+ }
+ return (0);
+
+fail: zyd_free_tx_list(sc);
+ return (error);
+}
+
+static void
+zyd_free_tx_list(struct zyd_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < ZYD_TX_LIST_CNT; i++) {
+ struct zyd_tx_data *data = &sc->sc_txdata[i];
+
+ if (data->xfer != NULL) {
+ usbd_free_xfer(data->xfer);
+ data->xfer = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static int
+zyd_alloc_rx_list(struct zyd_softc *sc)
+{
+ int i, error;
+
+ for (i = 0; i < ZYD_RX_LIST_CNT; i++) {
+ struct zyd_rx_data *data = &sc->sc_rxdata[i];
+
+ data->sc = sc; /* backpointer for callbacks */
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx xfer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ data->buf = usbd_alloc_buffer(data->xfer, ZYX_MAX_RXBUFSZ);
+ if (data->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ }
+ return (0);
+
+fail: zyd_free_rx_list(sc);
+ return (error);
+}
+
+static void
+zyd_free_rx_list(struct zyd_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < ZYD_RX_LIST_CNT; i++) {
+ struct zyd_rx_data *data = &sc->sc_rxdata[i];
+
+ if (data->xfer != NULL) {
+ usbd_free_xfer(data->xfer);
+ data->xfer = NULL;
+ }
+ }
+}
+
+/* ARGUSED */
+static struct ieee80211_node *
+zyd_node_alloc(struct ieee80211vap *vap __unused,
+ const uint8_t mac[IEEE80211_ADDR_LEN] __unused)
+{
+ struct zyd_node *zn;
+
+ zn = malloc(sizeof(struct zyd_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return (zn != NULL) ? (&zn->ni) : (NULL);
+}
+
+static void
+zyd_task(void *arg)
+{
+ int error;
+ struct zyd_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_AUTH:
+ zyd_set_chan(sc, ic->ic_curchan);
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_opmode == IEEE80211_M_MONITOR)
+ break;
+
+ /* turn link LED on */
+ error = zyd_set_led(sc, ZYD_LED1, 1);
+ if (error != 0)
+ goto fail;
+
+ /* make data LED blink upon Tx */
+ zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1);
+
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ zyd_set_bssid(sc, sc->sc_bssid);
+ break;
+ default:
+ break;
+ }
+
+fail:
+ IEEE80211_LOCK(ic);
+ zvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+}
+
+static int
+zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ callout_stop(&sc->sc_watchdog_ch);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+
+ if (nstate == IEEE80211_S_INIT) {
+ zvp->newstate(vap, nstate, arg);
+ return (0);
+ } else {
+ usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
+ return (EINPROGRESS);
+ }
+}
+
+static int
+zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen,
+ void *odata, int olen, u_int flags)
+{
+ usbd_xfer_handle xfer;
+ struct zyd_cmd cmd;
+ struct zyd_rq rq;
+ uint16_t xferflags;
+ usbd_status error;
+
+ if (sc->sc_flags & ZYD_FLAG_DETACHING)
+ return (ENXIO);
+
+ if ((xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL)
+ return (ENOMEM);
+
+ cmd.code = htole16(code);
+ bcopy(idata, cmd.data, ilen);
+
+ xferflags = USBD_FORCE_SHORT_XFER;
+ if (!(flags & ZYD_CMD_FLAG_READ))
+ xferflags |= USBD_SYNCHRONOUS;
+ else {
+ rq.idata = idata;
+ rq.odata = odata;
+ rq.len = olen / sizeof(struct zyd_pair);
+ STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq);
+ }
+
+ usbd_setup_xfer(xfer, sc->sc_ep[ZYD_ENDPT_IOUT], 0, &cmd,
+ sizeof(uint16_t) + ilen, xferflags, ZYD_INTR_TIMEOUT, NULL);
+ error = usbd_transfer(xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ device_printf(sc->sc_dev, "could not send command (error=%s)\n",
+ usbd_errstr(error));
+ (void)usbd_free_xfer(xfer);
+ return (EIO);
+ }
+ if (!(flags & ZYD_CMD_FLAG_READ)) {
+ (void)usbd_free_xfer(xfer);
+ return (0); /* write: don't wait for reply */
+ }
+ /* wait at most one second for command reply */
+ error = tsleep(odata, PCATCH, "zydcmd", hz);
+ if (error == EWOULDBLOCK)
+ device_printf(sc->sc_dev, "zyd_read sleep timeout\n");
+ STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq);
+
+ (void)usbd_free_xfer(xfer);
+ return (error);
+}
+
+static int
+zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val)
+{
+ struct zyd_pair tmp;
+ int error;
+
+ reg = htole16(reg);
+ error = zyd_cmd(sc, ZYD_CMD_IORD, &reg, sizeof(reg), &tmp, sizeof(tmp),
+ ZYD_CMD_FLAG_READ);
+ if (error == 0)
+ *val = le16toh(tmp.val);
+ return (error);
+}
+
+static int
+zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val)
+{
+ struct zyd_pair tmp[2];
+ uint16_t regs[2];
+ int error;
+
+ regs[0] = htole16(ZYD_REG32_HI(reg));
+ regs[1] = htole16(ZYD_REG32_LO(reg));
+ error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp),
+ ZYD_CMD_FLAG_READ);
+ if (error == 0)
+ *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val);
+ return (error);
+}
+
+static int
+zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct zyd_pair pair;
+
+ pair.reg = htole16(reg);
+ pair.val = htole16(val);
+
+ return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0);
+}
+
+static int
+zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val)
+{
+ struct zyd_pair pair[2];
+
+ pair[0].reg = htole16(ZYD_REG32_HI(reg));
+ pair[0].val = htole16(val >> 16);
+ pair[1].reg = htole16(ZYD_REG32_LO(reg));
+ pair[1].val = htole16(val & 0xffff);
+
+ return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0);
+}
+
+static int
+zyd_rfwrite(struct zyd_softc *sc, uint32_t val)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+ struct zyd_rfwrite_cmd req;
+ uint16_t cr203;
+ int error, i;
+
+ zyd_read16_m(sc, ZYD_CR203, &cr203);
+ cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA);
+
+ req.code = htole16(2);
+ req.width = htole16(rf->width);
+ for (i = 0; i < rf->width; i++) {
+ req.bit[i] = htole16(cr203);
+ if (val & (1 << (rf->width - 1 - i)))
+ req.bit[i] |= htole16(ZYD_RF_DATA);
+ }
+ error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0);
+fail:
+ return (error);
+}
+
+static int
+zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val)
+{
+ int error;
+
+ zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff);
+ zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff);
+ zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff);
+fail:
+ return (error);
+}
+
+static int
+zyd_lock_phy(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_MISC, &tmp);
+ tmp &= ~ZYD_UNLOCK_PHY_REGS;
+ zyd_write32_m(sc, ZYD_MAC_MISC, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_unlock_phy(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_MISC, &tmp);
+ tmp |= ZYD_UNLOCK_PHY_REGS;
+ zyd_write32_m(sc, ZYD_MAC_MISC, tmp);
+fail:
+ return (error);
+}
+
+/*
+ * RFMD RF methods.
+ */
+static int
+zyd_rfmd_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY;
+ static const uint32_t rfini[] = ZYD_RFMD_RF;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++) {
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+ }
+
+ /* init RFMD radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_rfmd_switch_radio(struct zyd_rf *rf, int on)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+
+ zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15);
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81);
+fail:
+ return (error);
+}
+
+static int
+zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_RFMD_CHANTABLE;
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+fail:
+ return (error);
+}
+
+/*
+ * AL2230 RF methods.
+ */
+static int
+zyd_al2230_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY;
+ static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT;
+ static const struct zyd_phy_pair phypll[] = {
+ { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f },
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }
+ };
+ static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1;
+ static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2;
+ static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) {
+ for (i = 0; i < N(phy2230s); i++)
+ zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val);
+ }
+
+ /* init AL2230 radio */
+ for (i = 0; i < N(rfini1); i++) {
+ error = zyd_rfwrite(sc, rfini1[i]);
+ if (error != 0)
+ goto fail;
+ }
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0)
+ error = zyd_rfwrite(sc, 0x000824);
+ else
+ error = zyd_rfwrite(sc, 0x0005a4);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < N(rfini2); i++) {
+ error = zyd_rfwrite(sc, rfini2[i]);
+ if (error != 0)
+ goto fail;
+ }
+
+ for (i = 0; i < N(phypll); i++)
+ zyd_write16_m(sc, phypll[i].reg, phypll[i].val);
+
+ for (i = 0; i < N(rfini3); i++) {
+ error = zyd_rfwrite(sc, rfini3[i]);
+ if (error != 0)
+ goto fail;
+ }
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_fini(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1;
+
+ for (i = 0; i < N(phy); i++)
+ zyd_write16_m(sc, phy[i].reg, phy[i].val);
+
+ if (sc->sc_newphy != 0)
+ zyd_write16_m(sc, ZYD_CR9, 0xe1);
+
+ zyd_write16_m(sc, ZYD_CR203, 0x6);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_init_b(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1;
+ static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2;
+ static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3;
+ static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B;
+ static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1;
+ static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2;
+ static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3;
+ static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE;
+ int i, error;
+
+ for (i = 0; i < N(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) {
+ for (i = 0; i < N(phy2230s); i++)
+ zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val);
+ }
+
+ for (i = 0; i < 3; i++) {
+ error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < N(rfini_part1); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part1[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0)
+ error = zyd_rfwrite(sc, 0x241000);
+ else
+ error = zyd_rfwrite(sc, 0x25a000);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < N(rfini_part2); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part2[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < N(phy2); i++)
+ zyd_write16_m(sc, phy2[i].reg, phy2[i].val);
+
+ for (i = 0; i < N(rfini_part3); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part3[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < N(phy3); i++)
+ zyd_write16_m(sc, phy3[i].reg, phy3[i].val);
+
+ error = zyd_al2230_fini(rf);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_switch_radio(struct zyd_rf *rf, int on)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f;
+
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04);
+ zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f);
+fail:
+ return (error);
+}
+
+static int
+zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = {
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 },
+ };
+ static const struct {
+ uint32_t r1, r2, r3;
+ } rfprog[] = ZYD_AL2230_CHANTABLE;
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r3);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < N(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1;
+ static const struct {
+ uint32_t r1, r2, r3;
+ } rfprog[] = ZYD_AL2230_CHANTABLE_B;
+
+ for (i = 0; i < N(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3);
+ if (error != 0)
+ goto fail;
+ error = zyd_al2230_fini(rf);
+fail:
+ return (error);
+#undef N
+}
+
+#define ZYD_AL2230_PHY_BANDEDGE6 \
+{ \
+ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \
+ { ZYD_CR47, 0x1e } \
+}
+
+static int
+zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error = 0, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6;
+ u_int chan = ieee80211_chan2ieee(ic, c);
+
+ if (chan == 1 || chan == 11)
+ r[0].val = 0x12;
+
+ for (i = 0; i < N(r); i++)
+ zyd_write16_m(sc, r[i].reg, r[i].val);
+fail:
+ return (error);
+#undef N
+}
+
+/*
+ * AL7230B RF methods.
+ */
+static int
+zyd_al7230B_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1;
+ static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2;
+ static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3;
+ static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1;
+ static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2;
+ int i, error;
+
+ /* for AL7230B, PHY and RF need to be initialized in "phases" */
+
+ /* init RF-dependent PHY registers, part one */
+ for (i = 0; i < N(phyini_1); i++)
+ zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val);
+
+ /* init AL7230B radio, part one */
+ for (i = 0; i < N(rfini_1); i++) {
+ if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0)
+ return (error);
+ }
+ /* init RF-dependent PHY registers, part two */
+ for (i = 0; i < N(phyini_2); i++)
+ zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val);
+
+ /* init AL7230B radio, part two */
+ for (i = 0; i < N(rfini_2); i++) {
+ if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0)
+ return (error);
+ }
+ /* init RF-dependent PHY registers, part three */
+ for (i = 0; i < N(phyini_3); i++)
+ zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al7230B_switch_radio(struct zyd_rf *rf, int on)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04);
+ zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f);
+fail:
+ return (error);
+}
+
+static int
+zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_AL7230B_CHANTABLE;
+ static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL;
+ int i, error;
+
+ zyd_write16_m(sc, ZYD_CR240, 0x57);
+ zyd_write16_m(sc, ZYD_CR251, 0x2f);
+
+ for (i = 0; i < N(rfsc); i++) {
+ if ((error = zyd_rfwrite(sc, rfsc[i])) != 0)
+ return (error);
+ }
+
+ zyd_write16_m(sc, ZYD_CR128, 0x14);
+ zyd_write16_m(sc, ZYD_CR129, 0x12);
+ zyd_write16_m(sc, ZYD_CR130, 0x10);
+ zyd_write16_m(sc, ZYD_CR38, 0x38);
+ zyd_write16_m(sc, ZYD_CR136, 0xdf);
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, 0x3c9000);
+ if (error != 0)
+ goto fail;
+
+ zyd_write16_m(sc, ZYD_CR251, 0x3f);
+ zyd_write16_m(sc, ZYD_CR203, 0x06);
+ zyd_write16_m(sc, ZYD_CR240, 0x08);
+fail:
+ return (error);
+#undef N
+}
+
+/*
+ * AL2210 RF methods.
+ */
+static int
+zyd_al2210_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY;
+ static const uint32_t rfini[] = ZYD_AL2210_RF;
+ uint32_t tmp;
+ int i, error;
+
+ zyd_write32_m(sc, ZYD_CR18, 2);
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ /* init AL2210 radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_write32_m(sc, ZYD_CR18, 3);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2210_switch_radio(struct zyd_rf *rf, int on)
+{
+ /* vendor driver does nothing for this RF chip */
+
+ return (0);
+}
+
+static int
+zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE;
+ uint32_t tmp;
+
+ zyd_write32_m(sc, ZYD_CR18, 2);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+
+ /* actually set the channel */
+ error = zyd_rfwrite(sc, rfprog[chan - 1]);
+ if (error != 0)
+ goto fail;
+
+ zyd_write32_m(sc, ZYD_CR18, 3);
+fail:
+ return (error);
+}
+
+/*
+ * GCT RF methods.
+ */
+static int
+zyd_gct_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY;
+ static const uint32_t rfini[] = ZYD_GCT_RF;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ /* init cgt radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_gct_switch_radio(struct zyd_rf *rf, int on)
+{
+ /* vendor driver does nothing for this RF chip */
+
+ return (0);
+}
+
+static int
+zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE;
+
+ error = zyd_rfwrite(sc, 0x1c0000);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1]);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, 0x1c0008);
+fail:
+ return (error);
+}
+
+/*
+ * Maxim RF methods.
+ */
+static int
+zyd_maxim_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM_RF;
+ uint16_t tmp;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* init maxim radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_maxim_switch_radio(struct zyd_rf *rf, int on)
+{
+
+ /* vendor driver does nothing for this RF chip */
+ return (0);
+}
+
+static int
+zyd_maxim_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM_RF;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_MAXIM_CHANTABLE;
+ uint16_t tmp;
+ int i, error;
+
+ /*
+ * Do the same as we do when initializing it, except for the channel
+ * values coming from the two channel tables.
+ */
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* first two values taken from the chantables */
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+ /* init maxim radio - skipping the two first values */
+ for (i = 2; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+/*
+ * Maxim2 RF methods.
+ */
+static int
+zyd_maxim2_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM2_RF;
+ uint16_t tmp;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* init maxim2 radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_maxim2_switch_radio(struct zyd_rf *rf, int on)
+{
+
+ /* vendor driver does nothing for this RF chip */
+ return (0);
+}
+
+static int
+zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM2_RF;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_MAXIM2_CHANTABLE;
+ uint16_t tmp;
+ int i, error;
+
+ /*
+ * Do the same as we do when initializing it, except for the channel
+ * values coming from the two channel tables.
+ */
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* first two values taken from the chantables */
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+ /* init maxim2 radio - skipping the two first values */
+ for (i = 2; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_rf_attach(struct zyd_softc *sc, uint8_t type)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+
+ rf->rf_sc = sc;
+
+ switch (type) {
+ case ZYD_RF_RFMD:
+ rf->init = zyd_rfmd_init;
+ rf->switch_radio = zyd_rfmd_switch_radio;
+ rf->set_channel = zyd_rfmd_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL2230:
+ case ZYD_RF_AL2230S:
+ if (sc->sc_macrev == ZYD_ZD1211B) {
+ rf->init = zyd_al2230_init_b;
+ rf->set_channel = zyd_al2230_set_channel_b;
+ } else {
+ rf->init = zyd_al2230_init;
+ rf->set_channel = zyd_al2230_set_channel;
+ }
+ rf->switch_radio = zyd_al2230_switch_radio;
+ rf->bandedge6 = zyd_al2230_bandedge6;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL7230B:
+ rf->init = zyd_al7230B_init;
+ rf->switch_radio = zyd_al7230B_switch_radio;
+ rf->set_channel = zyd_al7230B_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL2210:
+ rf->init = zyd_al2210_init;
+ rf->switch_radio = zyd_al2210_switch_radio;
+ rf->set_channel = zyd_al2210_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_GCT:
+ rf->init = zyd_gct_init;
+ rf->switch_radio = zyd_gct_switch_radio;
+ rf->set_channel = zyd_gct_set_channel;
+ rf->width = 21; /* 21-bit RF values */
+ break;
+ case ZYD_RF_MAXIM_NEW:
+ rf->init = zyd_maxim_init;
+ rf->switch_radio = zyd_maxim_switch_radio;
+ rf->set_channel = zyd_maxim_set_channel;
+ rf->width = 18; /* 18-bit RF values */
+ break;
+ case ZYD_RF_MAXIM_NEW2:
+ rf->init = zyd_maxim2_init;
+ rf->switch_radio = zyd_maxim2_switch_radio;
+ rf->set_channel = zyd_maxim2_set_channel;
+ rf->width = 18; /* 18-bit RF values */
+ break;
+ default:
+ device_printf(sc->sc_dev,
+ "sorry, radio \"%s\" is not supported yet\n",
+ zyd_rf_name(type));
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static const char *
+zyd_rf_name(uint8_t type)
+{
+ static const char * const zyd_rfs[] = {
+ "unknown", "unknown", "UW2451", "UCHIP", "AL2230",
+ "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT",
+ "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2",
+ "PHILIPS"
+ };
+
+ return zyd_rfs[(type > 15) ? 0 : type];
+}
+
+static int
+zyd_hw_init(struct zyd_softc *sc)
+{
+ int error;
+ const struct zyd_phy_pair *phyp;
+ struct zyd_rf *rf = &sc->sc_rf;
+ uint16_t val;
+
+ /* specify that the plug and play is finished */
+ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1);
+ zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase);
+ DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n",
+ sc->sc_fwbase);
+
+ /* retrieve firmware revision number */
+ zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev);
+ zyd_write32_m(sc, ZYD_CR_GPI_EN, 0);
+ zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f);
+ /* set mandatory rates - XXX assumes 802.11b/g */
+ zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f);
+
+ /* disable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0);
+
+ if ((error = zyd_read_pod(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ goto fail;
+ }
+
+ /* PHY init (resetting) */
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy;
+ for (; phyp->reg != 0; phyp++)
+ zyd_write16_m(sc, phyp->reg, phyp->val);
+ if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) {
+ zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val);
+ zyd_write32_m(sc, ZYD_CR157, val >> 8);
+ }
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ /* HMAC init */
+ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020);
+ zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808);
+ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000);
+ zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4);
+ zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f);
+ zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401);
+ zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080);
+ zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100);
+ zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070);
+ zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000);
+ zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203);
+ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1);
+ zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114);
+ zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032);
+ zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3);
+
+ if (sc->sc_macrev == ZYD_ZD1211) {
+ zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002);
+ zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640);
+ } else {
+ zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f);
+ zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028);
+ zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C);
+ zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824);
+ zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff);
+ }
+
+ /* init beacon interval to 100ms */
+ if ((error = zyd_set_beacon_interval(sc, 100)) != 0)
+ goto fail;
+
+ if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) {
+ device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n",
+ sc->sc_rfrev);
+ goto fail;
+ }
+
+ /* RF chip init */
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ error = (*rf->init)(rf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "radio initialization failed, error %d\n", error);
+ goto fail;
+ }
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ if ((error = zyd_read_eeprom(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ goto fail;
+ }
+
+fail: return (error);
+}
+
+static int
+zyd_read_pod(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp);
+ sc->sc_rfrev = tmp & 0x0f;
+ sc->sc_ledtype = (tmp >> 4) & 0x01;
+ sc->sc_al2230s = (tmp >> 7) & 0x01;
+ sc->sc_cckgain = (tmp >> 8) & 0x01;
+ sc->sc_fix_cr157 = (tmp >> 13) & 0x01;
+ sc->sc_parev = (tmp >> 16) & 0x0f;
+ sc->sc_bandedge6 = (tmp >> 21) & 0x01;
+ sc->sc_newphy = (tmp >> 31) & 0x01;
+ sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1;
+fail:
+ return (error);
+}
+
+static int
+zyd_read_eeprom(struct zyd_softc *sc)
+{
+ uint16_t val;
+ int error, i;
+
+ /* read Tx power calibration tables */
+ for (i = 0; i < 7; i++) {
+ zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val);
+ sc->sc_pwrcal[i * 2] = val >> 8;
+ sc->sc_pwrcal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val);
+ sc->sc_pwrint[i * 2] = val >> 8;
+ sc->sc_pwrint[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val);
+ sc->sc_ofdm36_cal[i * 2] = val >> 8;
+ sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val);
+ sc->sc_ofdm48_cal[i * 2] = val >> 8;
+ sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val);
+ sc->sc_ofdm54_cal[i * 2] = val >> 8;
+ sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff;
+ }
+fail:
+ return (error);
+}
+
+static int
+zyd_get_macaddr(struct zyd_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = ZYD_READFWDATAREQ;
+ USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, IEEE80211_ADDR_LEN);
+
+ error = usbd_do_request(sc->sc_udev, &req, sc->sc_bssid);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
+ }
+
+ return (error);
+}
+
+static int
+zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr)
+{
+ int error;
+ uint32_t tmp;
+
+ tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0];
+ zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp);
+ tmp = addr[5] << 8 | addr[4];
+ zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr)
+{
+ int error;
+ uint32_t tmp;
+
+ tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0];
+ zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp);
+ tmp = addr[5] << 8 | addr[4];
+ zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_switch_radio(struct zyd_softc *sc, int on)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+ int error;
+
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ error = (*rf->switch_radio)(rf, on);
+ if (error != 0)
+ goto fail;
+ error = zyd_unlock_phy(sc);
+fail:
+ return (error);
+}
+
+static int
+zyd_set_led(struct zyd_softc *sc, int which, int on)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp);
+ tmp &= ~which;
+ if (on)
+ tmp |= which;
+ zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp);
+fail:
+ return (error);
+}
+
+static void
+zyd_set_multi(void *arg)
+{
+ int error;
+ struct zyd_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifmultiaddr *ifma;
+ uint32_t low, high;
+ uint8_t v;
+
+ if (!(ifp->if_flags & IFF_UP))
+ return;
+
+ low = 0x00000000;
+ high = 0x80000000;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+ (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) {
+ low = 0xffffffff;
+ high = 0xffffffff;
+ } else {
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ v = ((uint8_t *)LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr))[5] >> 2;
+ if (v < 32)
+ low |= 1 << v;
+ else
+ high |= 1 << (v - 32);
+ }
+ IF_ADDR_UNLOCK(ifp);
+ }
+
+ /* reprogram multicast global hash table */
+ zyd_write32_m(sc, ZYD_MAC_GHTBL, low);
+ zyd_write32_m(sc, ZYD_MAC_GHTBH, high);
+fail:
+ if (error != 0)
+ device_printf(sc->sc_dev,
+ "could not set multicast hash table\n");
+}
+
+static void
+zyd_update_mcast(struct ifnet *ifp)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+
+ if (!(sc->sc_flags & ZYD_FLAG_INITDONE))
+ return;
+
+ usb_add_task(sc->sc_udev, &sc->sc_mcasttask, USB_TASKQ_DRIVER);
+}
+
+static int
+zyd_set_rxfilter(struct zyd_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t rxfilter;
+
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ rxfilter = ZYD_FILTER_BSS;
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_HOSTAP:
+ rxfilter = ZYD_FILTER_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ rxfilter = ZYD_FILTER_MONITOR;
+ break;
+ default:
+ /* should not get there */
+ return (EINVAL);
+ }
+ return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter);
+}
+
+static void
+zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c)
+{
+ int error;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct zyd_rf *rf = &sc->sc_rf;
+ uint32_t tmp;
+ u_int chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY) {
+ /* XXX should NEVER happen */
+ device_printf(sc->sc_dev,
+ "%s: invalid channel %x\n", __func__, chan);
+ return;
+ }
+
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ error = (*rf->set_channel)(rf, chan);
+ if (error != 0)
+ goto fail;
+
+ /* update Tx power */
+ zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]);
+
+ if (sc->sc_macrev == ZYD_ZD1211B) {
+ zyd_write16_m(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR69, 0x28);
+ zyd_write16_m(sc, ZYD_CR69, 0x2a);
+ }
+ if (sc->sc_cckgain) {
+ /* set CCK baseband gain from EEPROM */
+ if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0)
+ zyd_write16_m(sc, ZYD_CR47, tmp & 0xff);
+ }
+ if (sc->sc_bandedge6 && rf->bandedge6 != NULL) {
+ error = (*rf->bandedge6)(rf, c);
+ if (error != 0)
+ goto fail;
+ }
+ zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0);
+
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq =
+ htole16(c->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags =
+ htole16(c->ic_flags);
+fail:
+ return;
+}
+
+static int
+zyd_set_beacon_interval(struct zyd_softc *sc, int bintval)
+{
+ int error;
+ uint32_t val;
+
+ zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val);
+ sc->sc_atim_wnd = val;
+ zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val);
+ sc->sc_pre_tbtt = val;
+ sc->sc_bcn_int = bintval;
+
+ if (sc->sc_bcn_int <= 5)
+ sc->sc_bcn_int = 5;
+ if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int)
+ sc->sc_pre_tbtt = sc->sc_bcn_int - 1;
+ if (sc->sc_atim_wnd >= sc->sc_pre_tbtt)
+ sc->sc_atim_wnd = sc->sc_pre_tbtt - 1;
+
+ zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd);
+ zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt);
+ zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int);
+fail:
+ return (error);
+}
+
+static void
+zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct zyd_softc *sc = (struct zyd_softc *)priv;
+ struct zyd_cmd *cmd;
+ uint32_t datalen;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ if (status == USBD_STALLED) {
+ usbd_clear_endpoint_stall_async(
+ sc->sc_ep[ZYD_ENDPT_IIN]);
+ }
+ return;
+ }
+
+ cmd = (struct zyd_cmd *)sc->sc_ibuf;
+
+ if (le16toh(cmd->code) == ZYD_NOTIF_RETRYSTATUS) {
+ struct zyd_notif_retry *retry =
+ (struct zyd_notif_retry *)cmd->data;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni;
+
+ DPRINTF(sc, ZYD_DEBUG_TX_PROC,
+ "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n",
+ le16toh(retry->rate), ether_sprintf(retry->macaddr),
+ le16toh(retry->count) & 0xff, le16toh(retry->count));
+
+ /*
+ * Find the node to which the packet was sent and update its
+ * retry statistics. In BSS mode, this node is the AP we're
+ * associated to so no lookup is actually needed.
+ */
+ ni = ieee80211_find_txnode(vap, retry->macaddr);
+ if (ni != NULL) {
+ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn,
+ IEEE80211_AMRR_FAILURE, 1);
+ ieee80211_free_node(ni);
+ }
+ if (le16toh(retry->count) & 0x100)
+ ifp->if_oerrors++; /* too many retries */
+ } else if (le16toh(cmd->code) == ZYD_NOTIF_IORD) {
+ struct zyd_rq *rqp;
+
+ if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT)
+ return; /* HMAC interrupt */
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &datalen, NULL);
+ datalen -= sizeof(cmd->code);
+ datalen -= 2; /* XXX: padding? */
+
+ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) {
+ int i;
+
+ if (sizeof(struct zyd_pair) * rqp->len != datalen)
+ continue;
+ for (i = 0; i < rqp->len; i++) {
+ if (*(((const uint16_t *)rqp->idata) + i) !=
+ (((struct zyd_pair *)cmd->data) + i)->reg)
+ break;
+ }
+ if (i != rqp->len)
+ continue;
+
+ /* copy answer into caller-supplied buffer */
+ bcopy(cmd->data, rqp->odata,
+ sizeof(struct zyd_pair) * rqp->len);
+ wakeup(rqp->odata); /* wakeup caller */
+
+ return;
+ }
+ return; /* unexpected IORD notification */
+ } else {
+ device_printf(sc->sc_dev, "unknown notification %x\n",
+ le16toh(cmd->code));
+ }
+}
+
+static void
+zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_node *ni;
+ const struct zyd_plcphdr *plcp;
+ const struct zyd_rx_stat *stat;
+ struct mbuf *m;
+ int rlen, rssi, nf;
+
+ if (len < ZYD_MIN_FRAGSZ) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n",
+ device_get_nameunit(sc->sc_dev), len);
+ ifp->if_ierrors++;
+ return;
+ }
+
+ plcp = (const struct zyd_plcphdr *)buf;
+ stat = (const struct zyd_rx_stat *)
+ (buf + len - sizeof(struct zyd_rx_stat));
+
+ if (stat->flags & ZYD_RX_ERROR) {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: RX status indicated error (%x)\n",
+ device_get_nameunit(sc->sc_dev), stat->flags);
+ ifp->if_ierrors++;
+ return;
+ }
+
+ /* compute actual frame length */
+ rlen = len - sizeof(struct zyd_plcphdr) -
+ sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN;
+
+ /* allocate a mbuf to store the frame */
+ if (rlen > MHLEN)
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ else
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n",
+ device_get_nameunit(sc->sc_dev));
+ ifp->if_ierrors++;
+ return;
+ }
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = rlen;
+ bcopy((const uint8_t *)(plcp + 1), mtod(m, uint8_t *), rlen);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ if (stat->flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32))
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+ /* XXX toss, no way to express errors */
+ if (stat->flags & ZYD_RX_DECRYPTERR)
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+ tap->wr_rate = ieee80211_plcp2rate(plcp->signal,
+ (stat->flags & ZYD_RX_OFDM) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_antsignal = stat->rssi + -95;
+ tap->wr_antnoise = -95; /* XXX */
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+
+ rssi = stat->rssi > 63 ? 127 : 2 * stat->rssi;
+ nf = -95; /* XXX */
+
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void)ieee80211_input(ni, m, rssi, nf, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void)ieee80211_input_all(ic, m, rssi, nf, 0);
+}
+
+static void
+zyd_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct zyd_rx_data *data = priv;
+ struct zyd_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ const struct zyd_rx_desc *desc;
+ int len;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall(sc->sc_ep[ZYD_ENDPT_BIN]);
+
+ goto skip;
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ if (len < ZYD_MIN_RXBUFSZ) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: xfer too short (length=%d)\n",
+ device_get_nameunit(sc->sc_dev), len);
+ ifp->if_ierrors++; /* XXX not really errors */
+ goto skip;
+ }
+
+ desc = (const struct zyd_rx_desc *)
+ (data->buf + len - sizeof(struct zyd_rx_desc));
+
+ if (UGETW(desc->tag) == ZYD_TAG_MULTIFRAME) {
+ const uint8_t *p = data->buf, *end = p + len;
+ int i;
+
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: received multi-frame transfer\n", __func__);
+
+ for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) {
+ const uint16_t len16 = UGETW(desc->len[i]);
+
+ if (len16 == 0 || p + len16 > end)
+ break;
+
+ zyd_rx_data(sc, p, len16);
+ /* next frame is aligned on a 32-bit boundary */
+ p += (len16 + 3) & ~3;
+ }
+ } else {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: received single-frame transfer\n", __func__);
+
+ zyd_rx_data(sc, data->buf, len);
+ }
+
+skip: /* setup a new transfer */
+ usbd_setup_xfer(xfer, sc->sc_ep[ZYD_ENDPT_BIN], data, NULL,
+ ZYX_MAX_RXBUFSZ, USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, zyd_rxeof);
+ (void)usbd_transfer(xfer);
+}
+
+static uint8_t
+zyd_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12:
+ return (0xb);
+ case 18:
+ return (0xf);
+ case 24:
+ return (0xa);
+ case 36:
+ return (0xe);
+ case 48:
+ return (0x9);
+ case 72:
+ return (0xd);
+ case 96:
+ return (0x8);
+ case 108:
+ return (0xc);
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2:
+ return (0x0);
+ case 4:
+ return (0x1);
+ case 11:
+ return (0x2);
+ case 22:
+ return (0x3);
+ }
+ return (0xff); /* XXX unsupported/unknown rate */
+}
+
+static int
+zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct zyd_tx_desc *desc;
+ struct zyd_tx_data *data;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ int data_idx, rate, totlen, xferlen;
+ uint16_t pktlen;
+ usbd_status error;
+
+ data_idx = sc->sc_txidx;
+ sc->sc_txidx = (sc->sc_txidx + 1) % ZYD_TX_LIST_CNT;
+
+ data = &sc->sc_txdata[data_idx];
+ desc = (struct zyd_tx_desc *)data->buf;
+
+ rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ }
+
+ data->ni = ni;
+ data->m = m0;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ xferlen = sizeof(struct zyd_tx_desc) + m0->m_pkthdr.len;
+ totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ /* fill Tx descriptor */
+ desc->len = htole16(totlen);
+
+ desc->flags = ZYD_TX_FLAG_BACKOFF;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /* multicast frames are not sent at OFDM rates in 802.11b/g */
+ if (totlen > vap->iv_rtsthreshold) {
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ } else if (ZYD_RATE_IS_OFDM(rate) &&
+ (ic->ic_flags & IEEE80211_F_USEPROT)) {
+ if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF;
+ else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ }
+ } else
+ desc->flags |= ZYD_TX_FLAG_MULTICAST;
+
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
+ desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
+
+ desc->phy = zyd_plcp_signal(rate);
+ if (ZYD_RATE_IS_OFDM(rate)) {
+ desc->phy |= ZYD_TX_PHY_OFDM;
+ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
+ desc->phy |= ZYD_TX_PHY_5GHZ;
+ } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->phy |= ZYD_TX_PHY_SHPREAMBLE;
+
+ /* actual transmit length (XXX why +10?) */
+ pktlen = sizeof(struct zyd_tx_desc) + 10;
+ if (sc->sc_macrev == ZYD_ZD1211)
+ pktlen += totlen;
+ desc->pktlen = htole16(pktlen);
+
+ desc->plcp_length = (16 * totlen + rate - 1) / rate;
+ desc->plcp_service = 0;
+ if (rate == 22) {
+ const int remainder = (16 * totlen) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= ZYD_PLCP_LENGEXT;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ m_copydata(m0, 0, m0->m_pkthdr.len,
+ data->buf + sizeof(struct zyd_tx_desc));
+
+ DPRINTF(sc, ZYD_DEBUG_XMIT,
+ "%s: sending mgt frame len=%zu rate=%u xferlen=%u\n",
+ device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len,
+ rate, xferlen);
+
+ usbd_setup_xfer(data->xfer, sc->sc_ep[ZYD_ENDPT_BOUT], data,
+ data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
+ ZYD_TX_TIMEOUT, zyd_txeof);
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ ifp->if_oerrors++;
+ return (EIO);
+ }
+ sc->sc_txqueued++;
+
+ return (0);
+}
+
+static void
+zyd_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct zyd_tx_data *data = priv;
+ struct zyd_softc *sc = data->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
+
+ if (status == USBD_STALLED) {
+ usbd_clear_endpoint_stall_async(
+ sc->sc_ep[ZYD_ENDPT_BOUT]);
+ }
+ ifp->if_oerrors++;
+ return;
+ }
+
+ ni = data->ni;
+ /* update rate control statistics */
+ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn,
+ IEEE80211_AMRR_SUCCESS, 0);
+
+ /*
+ * Do any tx complete callback. Note this must
+ * be done before releasing the node reference.
+ */
+ m = data->m;
+ if (m != NULL && m->m_flags & M_TXCB) {
+ ieee80211_process_callback(ni, m, 0); /* XXX status? */
+ m_freem(m);
+ data->m = NULL;
+ }
+
+ ieee80211_free_node(ni);
+ data->ni = NULL;
+
+ ZYD_TX_LOCK(sc);
+ sc->sc_txqueued--;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ZYD_TX_UNLOCK(sc);
+
+ ifp->if_opackets++;
+ sc->sc_txtimer = 0;
+ zyd_start(ifp);
+}
+
+static int
+zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct zyd_tx_desc *desc;
+ struct zyd_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ int data_idx, rate, totlen, xferlen;
+ uint16_t pktlen;
+ usbd_status error;
+
+ data_idx = sc->sc_txidx;
+ sc->sc_txidx = (sc->sc_txidx + 1) % ZYD_TX_LIST_CNT;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ data = &sc->sc_txdata[data_idx];
+ desc = (struct zyd_tx_desc *)data->buf;
+
+ desc->flags = ZYD_TX_FLAG_BACKOFF;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ rate = tp->mcastrate;
+ desc->flags |= ZYD_TX_FLAG_MULTICAST;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
+ } else {
+ (void) ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn);
+ rate = ni->ni_txrate;
+ }
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ data->ni = ni;
+ data->m = NULL;
+
+ xferlen = sizeof(struct zyd_tx_desc) + m0->m_pkthdr.len;
+ totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ /* fill Tx descriptor */
+ desc->len = htole16(totlen);
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /* multicast frames are not sent at OFDM rates in 802.11b/g */
+ if (totlen > vap->iv_rtsthreshold) {
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ } else if (ZYD_RATE_IS_OFDM(rate) &&
+ (ic->ic_flags & IEEE80211_F_USEPROT)) {
+ if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF;
+ else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ }
+ }
+
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
+ desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
+
+ desc->phy = zyd_plcp_signal(rate);
+ if (ZYD_RATE_IS_OFDM(rate)) {
+ desc->phy |= ZYD_TX_PHY_OFDM;
+ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
+ desc->phy |= ZYD_TX_PHY_5GHZ;
+ } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->phy |= ZYD_TX_PHY_SHPREAMBLE;
+
+ /* actual transmit length (XXX why +10?) */
+ pktlen = sizeof(struct zyd_tx_desc) + 10;
+ if (sc->sc_macrev == ZYD_ZD1211)
+ pktlen += totlen;
+ desc->pktlen = htole16(pktlen);
+
+ desc->plcp_length = (16 * totlen + rate - 1) / rate;
+ desc->plcp_service = 0;
+ if (rate == 22) {
+ const int remainder = (16 * totlen) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= ZYD_PLCP_LENGEXT;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ m_copydata(m0, 0, m0->m_pkthdr.len,
+ data->buf + sizeof(struct zyd_tx_desc));
+
+ DPRINTF(sc, ZYD_DEBUG_XMIT,
+ "%s: sending data frame len=%zu rate=%u xferlen=%u\n",
+ device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len,
+ rate, xferlen);
+
+ m_freem(m0); /* mbuf no longer needed */
+
+ usbd_setup_xfer(data->xfer, sc->sc_ep[ZYD_ENDPT_BOUT], data,
+ data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
+ ZYD_TX_TIMEOUT, zyd_txeof);
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ ifp->if_oerrors++;
+ return (EIO);
+ }
+ sc->sc_txqueued++;
+
+ return (0);
+}
+
+static void
+zyd_start(struct ifnet *ifp)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ ZYD_TX_LOCK(sc);
+ for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->sc_txqueued >= ZYD_TX_LIST_CNT) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+ if (zyd_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
+
+ sc->sc_txtimer = 5;
+ }
+ ZYD_TX_UNLOCK(sc);
+}
+
+static int
+zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct zyd_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return (ENETDOWN);
+ }
+ ZYD_TX_LOCK(sc);
+ if (sc->sc_txqueued >= ZYD_TX_LIST_CNT) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return (ENOBUFS); /* XXX */
+ }
+
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ * XXX raw path
+ */
+ if (zyd_tx_mgt(sc, m, ni) != 0) {
+ ZYD_TX_UNLOCK(sc);
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ return (EIO);
+ }
+
+ ZYD_TX_UNLOCK(sc);
+ ifp->if_opackets++;
+ sc->sc_txtimer = 5;
+ return (0);
+}
+
+static void
+zyd_watchdog(void *arg)
+{
+ struct zyd_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ if (sc->sc_txtimer > 0) {
+ if (--sc->sc_txtimer == 0) {
+ device_printf(sc->sc_dev, "device timeout\n");
+ /* zyd_init(ifp); XXX needs a process context ? */
+ ifp->if_oerrors++;
+ return;
+ }
+ callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc);
+ }
+}
+
+static int
+zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ ZYD_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ sc->sc_if_flags) &
+ (IFF_ALLMULTI | IFF_PROMISC))
+ zyd_set_multi(sc);
+ } else {
+ zyd_init_locked(sc);
+ startall = 1;
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ zyd_stop(sc, 1);
+ }
+ sc->sc_if_flags = ifp->if_flags;
+ ZYD_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCGIFADDR:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+static void
+zyd_init_locked(struct zyd_softc *sc)
+{
+ int error, i;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t val;
+
+ if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) {
+ error = zyd_loadfirmware(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not load firmware (error=%d)\n", error);
+ goto fail;
+ }
+
+ error = usbd_set_config_no(sc->sc_udev, ZYD_CONFIG_NO, 1);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "setting config no failed\n");
+ goto fail;
+ }
+ error = usbd_device2interface_handle(sc->sc_udev,
+ ZYD_IFACE_INDEX, &sc->sc_iface);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "getting interface handle failed\n");
+ goto fail;
+ }
+
+ if ((error = zyd_open_pipes(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not open pipes\n");
+ goto fail;
+ }
+ if ((error = zyd_hw_init(sc)) != 0) {
+ device_printf(sc->sc_dev,
+ "hardware initialization failed\n");
+ goto fail;
+ }
+
+ device_printf(sc->sc_dev,
+ "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x "
+ "BE%x NP%x Gain%x F%x\n",
+ (sc->sc_macrev == ZYD_ZD1211) ? "": "B",
+ sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff,
+ zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev,
+ sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy,
+ sc->sc_cckgain, sc->sc_fix_cr157);
+
+ /* read regulatory domain (currently unused) */
+ zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val);
+ sc->sc_regdomain = val >> 16;
+ DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n",
+ sc->sc_regdomain);
+
+ /* we'll do software WEP decryption for now */
+ DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n",
+ __func__);
+ zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER);
+
+ sc->sc_flags |= ZYD_FLAG_INITONCE;
+ }
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ zyd_stop(sc, 0);
+
+ /* reset softc variables. */
+ sc->sc_txidx = 0;
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %s\n",
+ ether_sprintf(ic->ic_myaddr));
+ error = zyd_set_macaddr(sc, ic->ic_myaddr);
+ if (error != 0)
+ return;
+
+ /* set basic rates */
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003);
+ else if (ic->ic_curmode == IEEE80211_MODE_11A)
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500);
+ else /* assumes 802.11b/g */
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f);
+
+ /* promiscuous mode */
+ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0);
+ /* multicast setup */
+ zyd_set_multi(sc);
+ /* set RX filter */
+ error = zyd_set_rxfilter(sc);
+ if (error != 0)
+ goto fail;
+
+ /* switch radio transmitter ON */
+ error = zyd_switch_radio(sc, 1);
+ if (error != 0)
+ goto fail;
+ /* set default BSS channel */
+ zyd_set_chan(sc, ic->ic_curchan);
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ if ((error = zyd_alloc_tx_list(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not allocate Tx list\n");
+ goto fail;
+ }
+ if ((error = zyd_alloc_rx_list(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not allocate Rx list\n");
+ goto fail;
+ }
+
+ /*
+ * Start up the receive pipe.
+ */
+ for (i = 0; i < ZYD_RX_LIST_CNT; i++) {
+ struct zyd_rx_data *data = &sc->sc_rxdata[i];
+
+ usbd_setup_xfer(data->xfer, sc->sc_ep[ZYD_ENDPT_BIN], data,
+ NULL, ZYX_MAX_RXBUFSZ, USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, zyd_rxeof);
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ device_printf(sc->sc_dev,
+ "could not queue Rx transfer\n");
+ goto fail;
+ }
+ }
+
+ /* enable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK);
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ sc->sc_flags |= ZYD_FLAG_INITDONE;
+
+ callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc);
+ return;
+
+fail: zyd_stop(sc, 1);
+ return;
+}
+
+static void
+zyd_init(void *priv)
+{
+ struct zyd_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ ZYD_LOCK(sc);
+ zyd_init_locked(sc);
+ ZYD_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+zyd_stop(struct zyd_softc *sc, int disable)
+{
+ int error;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ sc->sc_txtimer = 0;
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ /* switch radio transmitter OFF */
+ error = zyd_switch_radio(sc, 0);
+ if (error != 0)
+ goto fail;
+ /* disable Rx */
+ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0);
+ /* disable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0);
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ callout_stop(&sc->sc_watchdog_ch);
+
+ usbd_abort_pipe(sc->sc_ep[ZYD_ENDPT_BIN]);
+ usbd_abort_pipe(sc->sc_ep[ZYD_ENDPT_BOUT]);
+
+ zyd_free_rx_list(sc);
+ zyd_free_tx_list(sc);
+fail:
+ return;
+}
+
+static int
+zyd_loadfirmware(struct zyd_softc *sc)
+{
+ usb_device_request_t req;
+ size_t size;
+ u_char *fw;
+ uint8_t stat;
+ uint16_t addr;
+
+ if (sc->sc_flags & ZYD_FLAG_FWLOADED)
+ return (0);
+
+ if (sc->sc_macrev == ZYD_ZD1211) {
+ fw = (u_char *)zd1211_firmware;
+ size = sizeof(zd1211_firmware);
+ } else {
+ fw = (u_char *)zd1211b_firmware;
+ size = sizeof(zd1211b_firmware);
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = ZYD_DOWNLOADREQ;
+ USETW(req.wIndex, 0);
+
+ addr = ZYD_FIRMWARE_START_ADDR;
+ while (size > 0) {
+ /*
+ * When the transfer size is 4096 bytes, it is not
+ * likely to be able to transfer it.
+ * The cause is port or machine or chip?
+ */
+ const int mlen = min(size, 64);
+
+ DPRINTF(sc, ZYD_DEBUG_FW,
+ "loading firmware block: len=%d, addr=0x%x\n", mlen, addr);
+
+ USETW(req.wValue, addr);
+ USETW(req.wLength, mlen);
+ if (usbd_do_request(sc->sc_udev, &req, fw) != 0)
+ return (EIO);
+
+ addr += mlen / 2;
+ fw += mlen;
+ size -= mlen;
+ }
+
+ /* check whether the upload succeeded */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = ZYD_DOWNLOADSTS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(stat));
+ if (usbd_do_request(sc->sc_udev, &req, &stat) != 0)
+ return (EIO);
+
+ sc->sc_flags |= ZYD_FLAG_FWLOADED;
+
+ return (stat & 0x80) ? (EIO) : (0);
+}
+
+static void
+zyd_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni);
+}
+
+static void
+zyd_scan_start(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = ZYD_SCAN_START;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+zyd_scan_end(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = ZYD_SCAN_END;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+zyd_set_channel(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+ /* do it in a process context */
+ sc->sc_scan_action = ZYD_SET_CHANNEL;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+zyd_scantask(void *arg)
+{
+ struct zyd_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ ZYD_LOCK(sc);
+
+ switch (sc->sc_scan_action) {
+ case ZYD_SCAN_START:
+ /* want broadcast address while scanning */
+ zyd_set_bssid(sc, ifp->if_broadcastaddr);
+ break;
+ case ZYD_SCAN_END:
+ /* restore previous bssid */
+ zyd_set_bssid(sc, sc->sc_bssid);
+ break;
+ case ZYD_SET_CHANNEL:
+ zyd_set_chan(sc, ic->ic_curchan);
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown scan action %d\n",
+ sc->sc_scan_action);
+ break;
+ }
+
+ ZYD_UNLOCK(sc);
+}
+
+static void
+zyd_wakeup(struct zyd_softc *sc)
+{
+ struct zyd_rq *rqp;
+
+ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq)
+ wakeup(rqp->odata); /* wakeup sleeping caller */
+}
+
+static device_method_t zyd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zyd_match),
+ DEVMETHOD(device_attach, zyd_attach),
+ DEVMETHOD(device_detach, zyd_detach),
+
+ { 0, 0 }
+};
+
+static driver_t zyd_driver = {
+ "zyd",
+ zyd_methods,
+ sizeof(struct zyd_softc)
+};
+
+static devclass_t zyd_devclass;
+
+DRIVER_MODULE(zyd, uhub, zyd_driver, zyd_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(zyd, wlan, 1, 1, 1);
+MODULE_DEPEND(zyd, wlan_amrr, 1, 1, 1);
+MODULE_DEPEND(zyd, usb, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/if_zydfw.h b/sys/legacy/dev/usb/if_zydfw.h
new file mode 100644
index 0000000..46f5c2a
--- /dev/null
+++ b/sys/legacy/dev/usb/if_zydfw.h
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (C) 2001, 2002, 2003,2004 ZyDAS Technology Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that the following conditions are met:
+ * 1. Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistribution 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/* $FreeBSD$ */
+
+uint8_t zd1211_firmware[] = {
+ 0x08, 0x91, 0xFF, 0xED, 0x09, 0x93, 0x1E, 0xEE,
+ 0xD1, 0x94, 0x11, 0xEE, 0x88, 0xD4, 0xD1, 0x96,
+ 0xD1, 0x98, 0x5C, 0x99, 0x5C, 0x99, 0x4C, 0x99,
+ 0x04, 0x9D, 0xD1, 0x98, 0xD1, 0x9A, 0x03, 0xEE,
+ 0xF4, 0x94, 0xD3, 0xD4, 0x41, 0x2A, 0x40, 0x4A,
+ 0x45, 0xBE, 0x88, 0x92, 0x41, 0x24, 0x40, 0x44,
+ 0x53, 0xBE, 0x40, 0xF0, 0x93, 0xEE, 0x41, 0xEE,
+ 0x98, 0x9A, 0xD4, 0xF7, 0x02, 0x00, 0x1F, 0xEC,
+ 0x00, 0x00, 0xB2, 0xF8, 0x4D, 0x00, 0xA1, 0xEC,
+ 0x00, 0x00, 0xA6, 0xF7, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xD8,
+ 0xA0, 0x90, 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8,
+ 0x40, 0xF0, 0xB4, 0xF0, 0xA0, 0x90, 0x98, 0x9A,
+ 0xA0, 0xD8, 0x40, 0xF0, 0x64, 0xEF, 0xA0, 0x90,
+ 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, 0xF6, 0xF0,
+ 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0,
+ 0xF7, 0xF6, 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8,
+ 0x40, 0xF0, 0xF8, 0xF5, 0xA0, 0x90, 0x98, 0x9A,
+ 0xA0, 0xD8, 0x40, 0xF0, 0xF1, 0xF0, 0xA0, 0x90,
+ 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0,
+ 0x97, 0xF7, 0xA0, 0x90, 0x98, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00,
+ 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x42, 0x02,
+ 0xC1, 0x92, 0x03, 0x96, 0x1B, 0xD7, 0x2A, 0x86,
+ 0x1A, 0xD5, 0x2B, 0x86, 0x09, 0xA3, 0x00, 0x80,
+ 0x19, 0xD3, 0x2C, 0x86, 0x00, 0xEE, 0x0A, 0x65,
+ 0xC0, 0x7A, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xFE, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, 0xC5, 0xD4,
+ 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x01, 0xD4,
+ 0x42, 0x02, 0xC1, 0x96, 0x0A, 0x65, 0xC0, 0x7A,
+ 0x02, 0x99, 0xC4, 0x92, 0x41, 0xA2, 0xC4, 0xD2,
+ 0xC5, 0x98, 0x1C, 0xD9, 0x2A, 0x86, 0x01, 0x98,
+ 0x1C, 0xD9, 0x2B, 0x86, 0x1B, 0xD7, 0x2C, 0x86,
+ 0x00, 0xEE, 0x09, 0xB3, 0xFE, 0xFF, 0xC2, 0xD2,
+ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x41, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0xE5, 0xEE, 0x11, 0x93, 0xD8, 0xF7, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xAE, 0xEE, 0x40, 0xF1,
+ 0x40, 0x92, 0x19, 0xD3, 0xD8, 0xF7, 0xC5, 0x92,
+ 0x41, 0x92, 0x19, 0xD3, 0x00, 0x83, 0x40, 0x92,
+ 0x19, 0xD3, 0x00, 0x83, 0x0F, 0x9F, 0x95, 0xF8,
+ 0x0F, 0x9F, 0x99, 0xEE, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x99, 0xEE, 0x40, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3,
+ 0x91, 0xEC, 0x40, 0xF0, 0x5F, 0xF2, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0F, 0x9F,
+ 0x99, 0xEE, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92,
+ 0x19, 0xD3, 0x12, 0x95, 0x19, 0xD3, 0x10, 0x95,
+ 0x19, 0xD3, 0x02, 0x80, 0x19, 0xD3, 0x03, 0x82,
+ 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, 0x91, 0xEC,
+ 0x40, 0xF0, 0x5F, 0xF2, 0x40, 0xF0, 0xDE, 0xF3,
+ 0x11, 0x93, 0x04, 0xEC, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xE3, 0xEE, 0x40, 0x92, 0x19, 0xD3,
+ 0x04, 0xEC, 0x40, 0xF0, 0x38, 0xF2, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x11, 0x93, 0x44, 0x96, 0x09, 0xB3, 0xFF, 0xFD,
+ 0x19, 0xD3, 0x44, 0x96, 0x40, 0xF0, 0x90, 0xF7,
+ 0x6E, 0x92, 0x19, 0xD3, 0x05, 0x84, 0x40, 0xF0,
+ 0xC4, 0xEE, 0x4B, 0x62, 0x0A, 0x95, 0x2E, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x2B, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x00, 0xEE, 0xD1, 0xD4, 0x0B, 0x97,
+ 0x2F, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x34, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x39, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x3E, 0xEE, 0xD1, 0xD4, 0x0B, 0x97,
+ 0x43, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x48, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x4D, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x4E, 0xEE, 0xC1, 0xD4, 0x0A, 0x65,
+ 0x00, 0x44, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2,
+ 0xC2, 0xD2, 0x43, 0xF1, 0x09, 0x93, 0x01, 0x3F,
+ 0x19, 0xD3, 0xC0, 0x85, 0x11, 0x93, 0x44, 0x96,
+ 0x09, 0xB3, 0xFF, 0xFC, 0x19, 0xD3, 0x44, 0x96,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, 0x03, 0x96,
+ 0x41, 0x02, 0x03, 0x99, 0xC4, 0x94, 0x42, 0x04,
+ 0xC1, 0x04, 0xC2, 0x94, 0xC3, 0xD4, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0x13, 0x97,
+ 0x95, 0xEC, 0x1B, 0xD7, 0x02, 0x80, 0x11, 0x93,
+ 0x99, 0xEC, 0x19, 0xD3, 0x7C, 0x96, 0x0B, 0x97,
+ 0xA0, 0x00, 0x1B, 0xD7, 0x6E, 0xEC, 0x0A, 0x65,
+ 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xFF, 0xBF, 0x11, 0xA3, 0x9A, 0xEC, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x14, 0x99, 0x03, 0x80, 0x0C, 0xB3, 0x00, 0x10,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x97, 0xF0,
+ 0x11, 0x93, 0x9F, 0xEC, 0x41, 0x02, 0x19, 0xD3,
+ 0x9F, 0xEC, 0x11, 0x93, 0xD6, 0xF7, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x84, 0xEF, 0x0A, 0x65,
+ 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0x00, 0x04, 0xC2, 0xD2, 0x0F, 0x9F, 0xB1, 0xF0,
+ 0x11, 0x93, 0x94, 0xEC, 0x02, 0xD2, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xD0, 0xEF, 0x41, 0x92,
+ 0x19, 0xD3, 0x94, 0xEC, 0x19, 0xD3, 0x9F, 0xEC,
+ 0x12, 0x95, 0x02, 0x80, 0x1A, 0xD5, 0x95, 0xEC,
+ 0x13, 0x97, 0x7C, 0x96, 0x1B, 0xD7, 0x99, 0xEC,
+ 0x0A, 0x65, 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0x00, 0x40, 0x19, 0xD3, 0x9A, 0xEC,
+ 0x09, 0x63, 0x00, 0x40, 0xC2, 0xD2, 0x02, 0x94,
+ 0x1A, 0xD5, 0x7C, 0x96, 0x0C, 0xB3, 0x00, 0x08,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xB0, 0xEF,
+ 0x0C, 0xB3, 0xFF, 0x07, 0x0F, 0x9F, 0xB4, 0xEF,
+ 0x11, 0x93, 0x06, 0x80, 0x09, 0xB3, 0xFF, 0x07,
+ 0x09, 0x03, 0x00, 0xA0, 0x19, 0xD3, 0x97, 0xEC,
+ 0x40, 0x98, 0x0B, 0x97, 0x9C, 0xEC, 0x04, 0x95,
+ 0x03, 0x05, 0x14, 0x03, 0x97, 0xEC, 0x46, 0x02,
+ 0xC1, 0x92, 0xC2, 0xD2, 0x41, 0x08, 0x42, 0x48,
+ 0x02, 0x9E, 0x0F, 0x9F, 0xBB, 0xEF, 0x11, 0x93,
+ 0x97, 0xEC, 0xC1, 0x92, 0xC5, 0xD2, 0x5F, 0xB2,
+ 0x19, 0xD3, 0x9B, 0xEC, 0x0F, 0x9F, 0xD3, 0xEF,
+ 0x13, 0x97, 0x98, 0xEC, 0xC5, 0xD6, 0x11, 0x93,
+ 0x03, 0x80, 0x09, 0xB3, 0x00, 0x08, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xE9, 0xEF, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x10,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x40, 0x98, 0x1C, 0xD9,
+ 0x9B, 0xEC, 0x12, 0x95, 0x9B, 0xEC, 0x40, 0x44,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x86, 0xF0, 0x0A, 0xB3,
+ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x07, 0xF0, 0x0A, 0xB3, 0x07, 0x00, 0x09, 0x05,
+ 0xA9, 0xEC, 0xC2, 0x94, 0x01, 0xD4, 0x09, 0x03,
+ 0xA1, 0xEC, 0xC1, 0x92, 0x19, 0xD3, 0x9B, 0xEC,
+ 0xC5, 0x94, 0x0A, 0xB5, 0x00, 0xFF, 0x01, 0xA5,
+ 0xC5, 0xD4, 0x0F, 0x9F, 0x13, 0xF0, 0x0A, 0x05,
+ 0xFF, 0xFF, 0x0A, 0x03, 0xB1, 0xEC, 0xC1, 0x92,
+ 0x01, 0xD2, 0x1A, 0xD5, 0x9B, 0xEC, 0xC5, 0x96,
+ 0x0B, 0x07, 0xFF, 0xFF, 0xC5, 0xD6, 0x11, 0x93,
+ 0x97, 0xEC, 0xC5, 0x98, 0xC1, 0xD8, 0x11, 0x93,
+ 0x97, 0xEC, 0x09, 0x05, 0x0B, 0x00, 0x03, 0xD4,
+ 0xC2, 0x96, 0x06, 0xD6, 0x7B, 0x95, 0x7A, 0x95,
+ 0x4C, 0x02, 0xC1, 0x92, 0x59, 0x93, 0x59, 0x93,
+ 0x01, 0xA5, 0x01, 0x98, 0x0C, 0xF5, 0x7B, 0x93,
+ 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xB3,
+ 0xFF, 0x00, 0x04, 0xD2, 0x5C, 0x93, 0x59, 0x93,
+ 0x04, 0x94, 0x01, 0xA5, 0x03, 0x96, 0xC3, 0xD4,
+ 0x11, 0x93, 0x97, 0xEC, 0x4C, 0x02, 0x05, 0xD2,
+ 0xC1, 0x92, 0x09, 0xB3, 0x00, 0xFF, 0x7C, 0x95,
+ 0x7A, 0x95, 0x02, 0xA3, 0x05, 0x98, 0xC4, 0xD2,
+ 0x12, 0x95, 0x97, 0xEC, 0x45, 0x04, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2,
+ 0x12, 0x95, 0x9B, 0xEC, 0x0A, 0xB3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5B, 0xF0,
+ 0x12, 0x95, 0x97, 0xEC, 0x4A, 0x04, 0x02, 0x99,
+ 0xC4, 0x92, 0x01, 0x98, 0x0C, 0xF3, 0x7B, 0x93,
+ 0x41, 0x02, 0x0F, 0x9F, 0x7C, 0xF0, 0x43, 0x44,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x7D, 0xF0, 0x11, 0x93,
+ 0x97, 0xEC, 0x42, 0x02, 0x0A, 0x05, 0xFF, 0xFF,
+ 0xC1, 0xD4, 0x11, 0x93, 0x97, 0xEC, 0x4A, 0x02,
+ 0x12, 0x95, 0x60, 0x96, 0xC1, 0xD4, 0x12, 0x95,
+ 0x97, 0xEC, 0x4B, 0x04, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0x1F, 0xFF, 0xC2, 0xD2, 0x12, 0x95,
+ 0x97, 0xEC, 0x4B, 0x04, 0x11, 0x93, 0x62, 0x96,
+ 0x41, 0x93, 0x59, 0x93, 0x02, 0x99, 0xC4, 0xA2,
+ 0xC2, 0xD2, 0xC5, 0x92, 0x19, 0xD3, 0x98, 0xEC,
+ 0x0A, 0x95, 0x0C, 0x02, 0x1A, 0xD5, 0x02, 0x80,
+ 0x0F, 0x9F, 0xB1, 0xF0, 0x09, 0x63, 0xFE, 0x7F,
+ 0x01, 0x97, 0xC3, 0x94, 0x0A, 0xA5, 0x00, 0x04,
+ 0xC1, 0xD4, 0x11, 0x93, 0x9F, 0xEC, 0x09, 0xA3,
+ 0x00, 0x01, 0x19, 0xD3, 0x9F, 0xEC, 0x40, 0xF0,
+ 0x39, 0xEF, 0x0F, 0x9F, 0xB1, 0xF0, 0x11, 0x93,
+ 0x94, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xA6, 0xF0, 0x40, 0xF0, 0x39, 0xEF, 0x11, 0x93,
+ 0x95, 0xEC, 0x44, 0xB2, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xB1, 0xF0, 0x48, 0x98, 0x1C, 0xD9,
+ 0x02, 0x80, 0x11, 0x93, 0x91, 0xEC, 0x41, 0x22,
+ 0x0A, 0x95, 0xB1, 0xF0, 0x88, 0xD4, 0x88, 0xDC,
+ 0x91, 0x9A, 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0x04, 0x82, 0x48, 0xB2, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xC8, 0xF0, 0x0A, 0x65, 0xFD, 0x7D,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFF, 0xFE,
+ 0xC2, 0xD2, 0x41, 0x92, 0x19, 0xD3, 0xBF, 0xEC,
+ 0x11, 0x93, 0x04, 0x82, 0x43, 0xB2, 0x12, 0x95,
+ 0x03, 0x82, 0x02, 0xB3, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xEF, 0xF0, 0x0A, 0xB3, 0x00, 0xFF,
+ 0x48, 0xA2, 0x19, 0xD3, 0x03, 0x82, 0x40, 0xF0,
+ 0xEB, 0xF3, 0x11, 0x93, 0xBF, 0xEC, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93,
+ 0x07, 0x82, 0x11, 0x43, 0x03, 0xEC, 0x02, 0x0E,
+ 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, 0x03, 0x82,
+ 0x09, 0xA3, 0x00, 0x01, 0x19, 0xD3, 0x03, 0x82,
+ 0x40, 0x96, 0x1B, 0xD7, 0xBF, 0xEC, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x11, 0x93, 0x20, 0xBC, 0xC8, 0xD2,
+ 0x40, 0xF0, 0x48, 0xF1, 0x41, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94,
+ 0x41, 0x02, 0xC1, 0x92, 0x01, 0x97, 0xC3, 0x96,
+ 0xC2, 0xD6, 0x0A, 0x45, 0x00, 0x95, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x45, 0xF1, 0xC1, 0x92, 0x41, 0xB2,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x45, 0xF1,
+ 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x45, 0xF1, 0x41, 0x98, 0x1C, 0xD9,
+ 0xC0, 0xEC, 0x12, 0x95, 0x02, 0x80, 0x01, 0xD4,
+ 0x40, 0xF0, 0x56, 0xF2, 0x0B, 0x67, 0xFD, 0x7D,
+ 0x03, 0x99, 0xC4, 0x92, 0x0C, 0x99, 0x96, 0x03,
+ 0x1C, 0xD9, 0x06, 0x82, 0x41, 0x98, 0x1C, 0xD9,
+ 0x02, 0x82, 0x42, 0x98, 0x1C, 0xD9, 0x05, 0x82,
+ 0x0C, 0x69, 0x80, 0x7F, 0x1C, 0xD9, 0x00, 0xB0,
+ 0x09, 0xA3, 0x00, 0x01, 0xC3, 0xD2, 0x01, 0x94,
+ 0x0A, 0xB3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x43, 0xF1, 0x42, 0xA4, 0x1A, 0xD5,
+ 0x02, 0x80, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x05, 0x92, 0xC5, 0xD2, 0x60, 0xB2, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x55, 0xF1, 0x40, 0xF0,
+ 0x35, 0xF7, 0xC5, 0x94, 0x0A, 0xB3, 0x10, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5E, 0xF1,
+ 0x40, 0xF0, 0x23, 0xF6, 0xC5, 0x96, 0x0B, 0xB3,
+ 0x40, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x67, 0xF1, 0x40, 0xF0, 0x5D, 0xF5, 0xC5, 0x94,
+ 0x0A, 0xB3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xC8, 0xF1, 0x13, 0x97, 0x21, 0xBC,
+ 0x01, 0xD6, 0x0B, 0xB3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x79, 0xF1, 0x40, 0xF0,
+ 0x62, 0xFB, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x82, 0xF1,
+ 0x40, 0xF0, 0x6C, 0xFB, 0x01, 0x96, 0x0B, 0xB3,
+ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xA2, 0xF1, 0x40, 0xF0, 0xB0, 0xFA, 0x41, 0x92,
+ 0x19, 0xD3, 0xD5, 0xF7, 0x11, 0x93, 0x03, 0xEC,
+ 0x09, 0x43, 0x40, 0x00, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x98, 0xF1, 0x40, 0x94, 0x1A, 0xD5, 0xD5, 0xF7,
+ 0x11, 0x93, 0x00, 0xEC, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xAB, 0xF1, 0x40, 0xF0, 0x38, 0xF2,
+ 0x0F, 0x9F, 0xAB, 0xF1, 0x01, 0x96, 0x0B, 0xB3,
+ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xAB, 0xF1, 0x40, 0xF0, 0x7C, 0xFB, 0x01, 0x94,
+ 0x0A, 0xB3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xB4, 0xF1, 0x40, 0xF0, 0x87, 0xFB,
+ 0x11, 0x93, 0x10, 0xEC, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xBF, 0xF1, 0x44, 0x96, 0x1B, 0xD7,
+ 0x0B, 0xBC, 0x0F, 0x9F, 0xC5, 0xF1, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xC5, 0xF1, 0x19, 0xD3,
+ 0x0B, 0xBC, 0x40, 0x92, 0x19, 0xD3, 0x10, 0xEC,
+ 0xC5, 0x94, 0x0A, 0xB3, 0x80, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, 0x13, 0x97,
+ 0x28, 0xBC, 0x01, 0xD6, 0x0B, 0xB3, 0x40, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xDA, 0xF1,
+ 0x40, 0xF0, 0x18, 0xF7, 0x01, 0x94, 0x0A, 0xB3,
+ 0x02, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xED, 0xF1, 0x40, 0xF0, 0xC4, 0xEE, 0x40, 0xF0,
+ 0x8F, 0xFB, 0x40, 0xF0, 0x1B, 0xF2, 0x40, 0x96,
+ 0x1B, 0xD7, 0x00, 0xEC, 0x41, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x09, 0xF2,
+ 0x40, 0xF0, 0x9E, 0xFB, 0x09, 0x63, 0x00, 0x44,
+ 0x01, 0x97, 0xC3, 0x94, 0x48, 0xA4, 0xC1, 0xD4,
+ 0x00, 0xEE, 0x40, 0x92, 0x19, 0xD3, 0x12, 0x95,
+ 0x19, 0xD3, 0x10, 0x95, 0x19, 0xD3, 0x02, 0x80,
+ 0x19, 0xD3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2,
+ 0x40, 0xF0, 0xAE, 0xFB, 0x0A, 0x65, 0x00, 0x44,
+ 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2,
+ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, 0xEA, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65,
+ 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEB, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF,
+ 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3,
+ 0x02, 0x80, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x93, 0x00, 0x09,
+ 0x19, 0xD3, 0x02, 0x80, 0x40, 0xF0, 0x56, 0xF2,
+ 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0xC8, 0xD2,
+ 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0,
+ 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0x3B, 0xF5, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x85, 0xF2, 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97,
+ 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0x92, 0xF2, 0x40, 0xF0, 0x94, 0xF2, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x92, 0xF2, 0xC8, 0xD2,
+ 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0,
+ 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0xF1, 0xBD, 0x19, 0xD3, 0xB6, 0xEC, 0x11, 0x93,
+ 0xB4, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xAC, 0xF2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xC3, 0x94, 0x0A, 0x07, 0x07, 0x00, 0xC1, 0xD6,
+ 0x0A, 0x05, 0x00, 0xA0, 0x1A, 0xD5, 0x96, 0xEC,
+ 0x11, 0x93, 0xB6, 0xEC, 0x19, 0xD3, 0x01, 0x80,
+ 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92,
+ 0x41, 0xA2, 0xC2, 0xD2, 0x40, 0x92, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x13, 0x97, 0xB4, 0xEC, 0x40, 0x46,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x2C, 0xF3, 0x12, 0x95,
+ 0x96, 0xEC, 0x0A, 0x03, 0x07, 0x00, 0xC1, 0x92,
+ 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0x09, 0x05,
+ 0x01, 0x00, 0x48, 0x02, 0xC1, 0x92, 0xC2, 0xD2,
+ 0x11, 0x93, 0x96, 0xEC, 0x4E, 0x02, 0xC1, 0x94,
+ 0xC5, 0xD6, 0xC5, 0x92, 0x11, 0x07, 0x96, 0xEC,
+ 0x0B, 0x03, 0x0F, 0x00, 0xC1, 0x98, 0x46, 0x06,
+ 0x7A, 0x93, 0x79, 0x93, 0x5C, 0x95, 0x5A, 0x95,
+ 0x02, 0xA3, 0xC3, 0xD2, 0x04, 0x95, 0xC5, 0x96,
+ 0x41, 0x06, 0xC5, 0xD6, 0x42, 0x46, 0x02, 0x9E,
+ 0x0F, 0x9F, 0xD5, 0xF2, 0x11, 0x93, 0x96, 0xEC,
+ 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xC1, 0x92,
+ 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0xC1, 0x92,
+ 0x09, 0xB5, 0x1F, 0x00, 0x43, 0x44, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x02, 0xF3, 0x40, 0x44, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x03, 0xF3, 0x0A, 0x05, 0xFF, 0xFF,
+ 0x0F, 0x9F, 0x03, 0xF3, 0x43, 0x94, 0x11, 0x93,
+ 0x96, 0xEC, 0x42, 0x02, 0xC1, 0xD4, 0x11, 0x93,
+ 0x96, 0xEC, 0x49, 0x02, 0xC1, 0x92, 0x19, 0xD3,
+ 0xB4, 0xEC, 0x09, 0x05, 0xF2, 0xFF, 0x1A, 0xD5,
+ 0x92, 0xEC, 0x09, 0x43, 0xD0, 0x07, 0x02, 0x9E,
+ 0x0F, 0x9F, 0x2C, 0xF3, 0x11, 0x93, 0xDC, 0xF7,
+ 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93,
+ 0xDB, 0xF7, 0x09, 0xA3, 0x40, 0x00, 0x19, 0xD3,
+ 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, 0x01, 0x95,
+ 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, 0x40, 0x96,
+ 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, 0x92, 0xF3,
+ 0x11, 0x93, 0x92, 0xEC, 0x12, 0x95, 0xB6, 0xEC,
+ 0x02, 0x43, 0x02, 0x8E, 0x0F, 0x9F, 0x7A, 0xF3,
+ 0x02, 0x0E, 0x0F, 0x9F, 0x4D, 0xF3, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x80, 0x00,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x95, 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC,
+ 0x40, 0x96, 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F,
+ 0x92, 0xF3, 0x11, 0x93, 0x03, 0x80, 0x09, 0xB3,
+ 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x5F, 0xF3, 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x5F, 0xF3, 0x40, 0xF0,
+ 0xA6, 0xF3, 0x0F, 0x9F, 0x94, 0xF3, 0x41, 0x92,
+ 0xC8, 0xD2, 0x0A, 0x95, 0x91, 0xEC, 0xC8, 0xD4,
+ 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, 0x11, 0x93,
+ 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x72, 0xF3, 0x42, 0x96, 0x1B, 0xD7, 0xC0, 0xEC,
+ 0x0F, 0x9F, 0x94, 0xF3, 0x0A, 0x65, 0xFE, 0x7F,
+ 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2,
+ 0x0F, 0x9F, 0x94, 0xF3, 0x12, 0x45, 0x03, 0xEC,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x8C, 0xF3, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x08,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x1A, 0xD5, 0x92, 0xEC,
+ 0x11, 0x93, 0x92, 0xEC, 0x19, 0x25, 0x92, 0xEC,
+ 0x09, 0x63, 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD,
+ 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, 0xA6, 0xF3,
+ 0x40, 0x92, 0xC8, 0xD2, 0x09, 0x93, 0x91, 0xEC,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x11, 0x93, 0xD7, 0xF7, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xB6, 0xF3, 0x0A, 0x65,
+ 0xBC, 0x69, 0x02, 0x97, 0xC3, 0x92, 0x09, 0x83,
+ 0x00, 0x02, 0xC2, 0xD2, 0x11, 0x93, 0x03, 0x80,
+ 0x09, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xC9, 0xF3, 0x11, 0x93, 0xDC, 0xF7,
+ 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93,
+ 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x20, 0x19, 0xD3,
+ 0xDB, 0xF7, 0x11, 0x93, 0xB5, 0xEC, 0x19, 0xD3,
+ 0x04, 0x80, 0x12, 0x95, 0xB4, 0xEC, 0x1A, 0xD5,
+ 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xC3, 0x96, 0x1B, 0xD7, 0xB5, 0xEC, 0x40, 0x94,
+ 0x1A, 0xD5, 0xB4, 0xEC, 0x19, 0xD3, 0xF2, 0xBD,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, 0xD3,
+ 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3,
+ 0x03, 0x82, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0x01, 0x82, 0xC5, 0xD2, 0x40, 0x94, 0x01, 0xD4,
+ 0x13, 0x97, 0xB8, 0xEC, 0x02, 0xD6, 0x03, 0x95,
+ 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x05, 0x13, 0x97,
+ 0x03, 0xEC, 0x01, 0x27, 0x02, 0x99, 0xC4, 0x92,
+ 0x03, 0x03, 0xC2, 0xD2, 0x14, 0x99, 0xBA, 0xEC,
+ 0x03, 0x09, 0x1C, 0xD9, 0xBA, 0xEC, 0x12, 0x95,
+ 0x04, 0x82, 0x0A, 0xB3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x29, 0xF5, 0x01, 0x92,
+ 0x03, 0xD2, 0x0A, 0xA3, 0x02, 0x00, 0x19, 0xD3,
+ 0x04, 0x82, 0x02, 0x96, 0x0B, 0x05, 0x01, 0x00,
+ 0x1A, 0xD5, 0xB8, 0xEC, 0xC5, 0x92, 0x43, 0x42,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x37, 0xF4, 0x42, 0x44,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x93,
+ 0xBF, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x37, 0xF4, 0x0C, 0x49, 0xD3, 0x08, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x63, 0x07, 0x82,
+ 0x11, 0xA3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93,
+ 0x79, 0x93, 0x79, 0x93, 0x03, 0xD2, 0xC5, 0x94,
+ 0x0A, 0xB5, 0xFC, 0xFF, 0x04, 0xD4, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x46, 0xF4,
+ 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x4D, 0xF4, 0xC5, 0x98, 0x0C, 0x03,
+ 0xFF, 0xFF, 0x42, 0x42, 0x02, 0x8E, 0x0F, 0x9F,
+ 0x74, 0xF4, 0x0A, 0x95, 0xBB, 0xEC, 0x42, 0x92,
+ 0x19, 0xD3, 0xB9, 0xEC, 0xC5, 0x96, 0x43, 0x46,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x66, 0xF4, 0x0B, 0x07,
+ 0xFC, 0xFF, 0xC5, 0xD6, 0xD2, 0x98, 0x1C, 0xD9,
+ 0xC8, 0xBC, 0xD2, 0x96, 0x1B, 0xD7, 0xCA, 0xBC,
+ 0x09, 0x03, 0xFF, 0xFF, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x52, 0xF4, 0x19, 0xD3, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x72, 0xF4,
+ 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xC2, 0xD2,
+ 0x0F, 0x9F, 0x74, 0xF4, 0x1A, 0xD5, 0x93, 0xEC,
+ 0x03, 0x98, 0x40, 0x48, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xA1, 0xF4, 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x84, 0xF4, 0x04, 0x94,
+ 0x48, 0x44, 0x02, 0x4E, 0x0F, 0x9F, 0x8F, 0xF4,
+ 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xA1, 0xF4,
+ 0x11, 0x93, 0x04, 0x82, 0x41, 0xB2, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xA1, 0xF4, 0x41, 0x96,
+ 0x01, 0xD6, 0x0A, 0x65, 0xBD, 0x43, 0x02, 0x99,
+ 0xC4, 0x92, 0x09, 0xA3, 0x80, 0x00, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0xFA, 0xF4, 0xC5, 0x98, 0x43, 0x48, 0x02, 0x9E,
+ 0x0F, 0x9F, 0xFA, 0xF4, 0x4F, 0x96, 0x0C, 0xB3,
+ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xAE, 0xF4, 0x47, 0x96, 0x11, 0x93, 0xB7, 0xEC,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4,
+ 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xD6, 0xF4, 0x12, 0x95, 0x00, 0x82,
+ 0x0A, 0x05, 0xFF, 0xAF, 0x05, 0xD4, 0xC8, 0xD6,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x7B, 0xF7, 0x42, 0x00,
+ 0x05, 0x96, 0xC3, 0x94, 0x01, 0xB5, 0x40, 0x44,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, 0x06, 0x98,
+ 0x50, 0x98, 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x98,
+ 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x92, 0x03, 0xD2,
+ 0x0F, 0x9F, 0xFF, 0xF4, 0x03, 0x94, 0x40, 0x44,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xE3, 0xF4, 0x0A, 0x65,
+ 0x5E, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x48, 0xA2,
+ 0xC2, 0xD2, 0x0F, 0x9F, 0xFF, 0xF4, 0x11, 0x93,
+ 0xB8, 0xEC, 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x03,
+ 0x04, 0x96, 0x13, 0x25, 0x03, 0xEC, 0xC1, 0xD4,
+ 0x11, 0x93, 0xBA, 0xEC, 0x19, 0x05, 0xBA, 0xEC,
+ 0x1B, 0xD7, 0x01, 0x82, 0x0A, 0x65, 0xFD, 0x7D,
+ 0x02, 0x99, 0xC4, 0x92, 0x43, 0xA2, 0xC2, 0xD2,
+ 0x41, 0x92, 0x01, 0xD2, 0x03, 0x94, 0x40, 0x44,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, 0x11, 0x93,
+ 0xB9, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x0B, 0xF5, 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3,
+ 0xBA, 0xEC, 0x19, 0xD3, 0xBB, 0xEC, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5,
+ 0x41, 0x98, 0x1C, 0xD9, 0xB7, 0xEC, 0x11, 0x93,
+ 0xBF, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x24, 0xF5, 0x11, 0x93, 0x00, 0x82, 0x19, 0xD3,
+ 0x02, 0x82, 0x0A, 0x65, 0xFD, 0x7D, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2,
+ 0x40, 0x98, 0x1C, 0xD9, 0xBF, 0xEC, 0x0F, 0x9F,
+ 0x2C, 0xF5, 0x01, 0x92, 0x19, 0xD3, 0xB7, 0xEC,
+ 0x01, 0x94, 0x40, 0x44, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x38, 0xF5, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2,
+ 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x12, 0x95, 0x03, 0x80,
+ 0x0A, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x57, 0xF5, 0x0A, 0xB7, 0x00, 0x08,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x5A, 0xF5,
+ 0x11, 0x93, 0x03, 0xEC, 0x41, 0x02, 0x09, 0xB3,
+ 0xFE, 0xFF, 0x12, 0x95, 0x07, 0x80, 0x01, 0x45,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x5A, 0xF5, 0x41, 0x92,
+ 0x0F, 0x9F, 0x5B, 0xF5, 0x40, 0x92, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2,
+ 0x13, 0x97, 0x6E, 0xEC, 0x0B, 0x47, 0xA0, 0x00,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x86, 0xF5, 0x09, 0x63,
+ 0x08, 0x43, 0x0A, 0x65, 0xFF, 0x5F, 0x01, 0x99,
+ 0xC4, 0xD4, 0x0A, 0x95, 0x9B, 0xEC, 0xD2, 0x96,
+ 0x1B, 0xD7, 0xFA, 0xBC, 0xD2, 0x96, 0xC4, 0xD6,
+ 0xD2, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, 0xD2, 0x96,
+ 0xC1, 0xD6, 0xC2, 0x94, 0x1A, 0xD5, 0xFA, 0xBC,
+ 0x0F, 0x9F, 0xC4, 0xF5, 0x0C, 0x69, 0xFF, 0x6F,
+ 0x1C, 0xD9, 0xF8, 0xBC, 0x0B, 0x47, 0x10, 0x95,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x9E, 0xF5, 0x0A, 0x95,
+ 0x6F, 0xEC, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99,
+ 0xC4, 0xD6, 0xD2, 0x96, 0x1B, 0xD7, 0xF8, 0xBC,
+ 0x0C, 0x69, 0xEE, 0x6A, 0xC1, 0xD8, 0xC2, 0x94,
+ 0x1A, 0xD5, 0xF8, 0xBC, 0x40, 0x92, 0xC5, 0xD2,
+ 0x11, 0x43, 0xC1, 0xEC, 0x02, 0x0E, 0x0F, 0x9F,
+ 0xC1, 0xF5, 0xC5, 0x94, 0x0A, 0x03, 0x71, 0xEC,
+ 0xC1, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, 0x11, 0x93,
+ 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xB3, 0xF5, 0x0A, 0x95, 0x6F, 0xEC, 0xC8, 0xD4,
+ 0x40, 0xF0, 0x9C, 0xF7, 0x19, 0xD3, 0xF8, 0xBC,
+ 0x41, 0x00, 0xC5, 0x96, 0x41, 0x06, 0xC5, 0xD6,
+ 0x13, 0x47, 0xC1, 0xEC, 0x02, 0x1E, 0x0F, 0x9F,
+ 0xA5, 0xF5, 0x40, 0x98, 0x1C, 0xD9, 0xFA, 0xBC,
+ 0x40, 0x92, 0x19, 0xD3, 0x6E, 0xEC, 0x19, 0xD3,
+ 0xC1, 0xEC, 0x0A, 0x65, 0x52, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3,
+ 0xBF, 0xFF, 0xC2, 0xD2, 0x41, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x43, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x06, 0x92, 0x01, 0xD2, 0x0A, 0x65,
+ 0xF0, 0x6A, 0x0B, 0x97, 0x6F, 0xEC, 0x02, 0x99,
+ 0xC4, 0x98, 0xD3, 0xD8, 0x02, 0xD6, 0x0A, 0x03,
+ 0x02, 0x00, 0x01, 0x97, 0xC3, 0x98, 0x02, 0x96,
+ 0xC3, 0xD8, 0x01, 0x96, 0xC1, 0xD6, 0x1A, 0xD5,
+ 0x6E, 0xEC, 0xC5, 0x98, 0x14, 0x99, 0x6F, 0xEC,
+ 0xC2, 0xD8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92,
+ 0xC8, 0xD2, 0x40, 0xF0, 0xD9, 0xF5, 0x41, 0x00,
+ 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x13, 0xF6, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x10, 0xF6, 0x0A, 0x65, 0xFE, 0x7F,
+ 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2,
+ 0x40, 0x92, 0x19, 0xD3, 0xC0, 0xEC, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xE9, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF,
+ 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x63, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0xAF, 0xBC, 0x47, 0xB2, 0x59, 0x95, 0x5A, 0x95,
+ 0x12, 0xA5, 0xBF, 0xBC, 0x0A, 0xB3, 0x01, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x35, 0xF6,
+ 0x41, 0x04, 0x05, 0x93, 0x40, 0x96, 0x20, 0xD6,
+ 0x62, 0x97, 0x0F, 0x9F, 0x44, 0xF6, 0x14, 0x99,
+ 0xFC, 0xBC, 0xD1, 0xD8, 0x14, 0x99, 0xFE, 0xBC,
+ 0xD1, 0xD8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xD8,
+ 0x20, 0x98, 0x03, 0x49, 0x02, 0x1E, 0x0F, 0x9F,
+ 0x3B, 0xF6, 0xC5, 0x92, 0x62, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x5D, 0xF6, 0x02, 0x8E, 0x0F, 0x9F,
+ 0x57, 0xF6, 0x61, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x81, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, 0x63, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xA4, 0xF6, 0x0F, 0x9F,
+ 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, 0x0C, 0x99,
+ 0x71, 0xEC, 0x0B, 0x05, 0xFF, 0xFF, 0x40, 0x96,
+ 0x0F, 0x9F, 0x6A, 0xF6, 0xD1, 0x96, 0xD4, 0xD6,
+ 0x20, 0x96, 0x41, 0x06, 0x20, 0xD6, 0x02, 0x47,
+ 0x02, 0x1E, 0x0F, 0x9F, 0x66, 0xF6, 0x1A, 0xD5,
+ 0xC1, 0xEC, 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x99,
+ 0xC4, 0x92, 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0xAE, 0xF6, 0x0A, 0x03, 0xFE, 0xFF, 0x61, 0x95,
+ 0x40, 0x98, 0x20, 0xD8, 0x02, 0x49, 0x02, 0x0E,
+ 0x0F, 0x9F, 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00,
+ 0x21, 0xD2, 0x20, 0x92, 0x05, 0x03, 0x42, 0x02,
+ 0xC8, 0xD2, 0x21, 0x96, 0xC3, 0x92, 0x42, 0x06,
+ 0x21, 0xD6, 0xC8, 0xD2, 0x22, 0xD4, 0x40, 0xF0,
+ 0x01, 0xF1, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08,
+ 0x20, 0xD8, 0x22, 0x94, 0x02, 0x49, 0x02, 0x1E,
+ 0x0F, 0x9F, 0x8D, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6,
+ 0x0D, 0x03, 0x03, 0x00, 0xC8, 0xD2, 0x02, 0x92,
+ 0xC8, 0xD2, 0x01, 0x96, 0xC8, 0xD6, 0x40, 0xF0,
+ 0xB1, 0xF6, 0x43, 0x00, 0x63, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x45, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x08, 0x00, 0x08, 0x94,
+ 0xC5, 0xD4, 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94,
+ 0x03, 0xD4, 0x42, 0x02, 0xC1, 0x92, 0x01, 0xD2,
+ 0x02, 0x97, 0xC5, 0x94, 0x0A, 0x83, 0xFF, 0xFF,
+ 0x11, 0xB3, 0x2C, 0x93, 0x09, 0xB3, 0xFB, 0xFF,
+ 0x19, 0xD3, 0x2C, 0x93, 0x03, 0x92, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xE4, 0xF6, 0x01, 0x94,
+ 0xD2, 0x92, 0x19, 0xD3, 0x2C, 0x93, 0x01, 0xD4,
+ 0x02, 0x94, 0x12, 0x95, 0x2C, 0x93, 0x44, 0xA4,
+ 0x1A, 0xD5, 0x2C, 0x93, 0x0A, 0xB5, 0xFB, 0xFF,
+ 0x1A, 0xD5, 0x2C, 0x93, 0x0B, 0x07, 0xFF, 0xFF,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0xCF, 0xF6,
+ 0x09, 0x63, 0xD4, 0x6C, 0x01, 0x95, 0xC2, 0x96,
+ 0xC5, 0x94, 0x02, 0xA7, 0xC1, 0xD6, 0x03, 0x92,
+ 0x54, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xF4, 0xF6,
+ 0x0A, 0x83, 0xFF, 0xFF, 0x1B, 0xB3, 0x2C, 0x93,
+ 0x45, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xD3, 0xF2, 0xBD, 0x40, 0xF0, 0x3B, 0xF5,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x08, 0xF7,
+ 0x40, 0xF0, 0x94, 0xF2, 0x0F, 0x9F, 0x16, 0xF7,
+ 0x40, 0x96, 0xC8, 0xD6, 0x09, 0x93, 0x91, 0xEC,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x0A, 0x65,
+ 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2,
+ 0xC2, 0xD2, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x0A, 0x65,
+ 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF,
+ 0xC2, 0xD2, 0x40, 0x92, 0x19, 0xD3, 0x2D, 0xBC,
+ 0x0A, 0x65, 0xD8, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x09, 0x63, 0xEA, 0x43, 0x01, 0x97, 0xC3, 0x94,
+ 0x44, 0xA4, 0xC1, 0xD4, 0x11, 0x93, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x6F, 0xF7,
+ 0x12, 0x95, 0x93, 0xEC, 0x0B, 0x67, 0x36, 0x43,
+ 0xD2, 0x98, 0x1C, 0xD9, 0xC8, 0xBC, 0xD2, 0x98,
+ 0x03, 0x93, 0xC1, 0xD8, 0x11, 0x93, 0xB9, 0xEC,
+ 0x09, 0x03, 0xFF, 0xFF, 0x19, 0xD3, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x48, 0xF7,
+ 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, 0xBA, 0xEC,
+ 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xCA, 0xD2,
+ 0xC2, 0xD2, 0x0A, 0x65, 0x5E, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEA, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3,
+ 0xFB, 0xFF, 0x0F, 0x9F, 0x78, 0xF7, 0x11, 0x93,
+ 0x03, 0xEC, 0x19, 0xD3, 0x01, 0x82, 0x0A, 0x65,
+ 0xFD, 0x7D, 0x02, 0x97, 0xC3, 0x92, 0x43, 0xA2,
+ 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x03, 0x92, 0x04, 0x96,
+ 0x0D, 0x5E, 0x50, 0x46, 0x02, 0x0E, 0x40, 0x92,
+ 0x09, 0xEE, 0x44, 0x46, 0x04, 0x0E, 0x59, 0x93,
+ 0x44, 0x26, 0x04, 0x5E, 0x46, 0xEE, 0x41, 0x93,
+ 0x41, 0x26, 0x43, 0x4E, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0xB1, 0xFE, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x03, 0x94,
+ 0x1A, 0xD5, 0xA3, 0xF7, 0x11, 0x93, 0x00, 0x90,
+ 0x88, 0x98, 0x90, 0x9A, 0x1D, 0x00, 0x1A, 0x00,
+ 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, 0x19, 0x00,
+ 0x1A, 0x00, 0x1B, 0x00, 0x16, 0x00, 0x21, 0x00,
+ 0x12, 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00,
+ 0x19, 0x00, 0x19, 0x00, 0x21, 0x00, 0x2D, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x69,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0xF2, 0xCD, 0xF7, 0x00, 0x00, 0x74, 0xF2,
+ 0xCD, 0xF7, 0x00, 0x00, 0xB9, 0xF2, 0xCA, 0xF7,
+ 0xD1, 0xF7, 0x00, 0x00, 0x97, 0xF3, 0xCD, 0xF7,
+ 0x05, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * current zd1211b firmware version.
+ */
+#define ZD1211B_FIRMWARE_VER 4705
+
+uint8_t zd1211b_firmware[] = {
+ 0x08, 0x91, 0xff, 0xed, 0x09, 0x93, 0x1e, 0xee, 0xd1, 0x94, 0x11,
+ 0xee, 0x88, 0xd4, 0xd1, 0x96, 0xd1, 0x98, 0x5c, 0x99, 0x5c, 0x99,
+ 0x4c, 0x99, 0x04, 0x9d, 0xd1, 0x98, 0xd1, 0x9a, 0x03, 0xee, 0xf4,
+ 0x94, 0xd3, 0xd4, 0x41, 0x2a, 0x40, 0x4a, 0x45, 0xbe, 0x88, 0x92,
+ 0x41, 0x24, 0x40, 0x44, 0x53, 0xbe, 0x40, 0xf0, 0x4e, 0xee, 0x41,
+ 0xee, 0x98, 0x9a, 0x72, 0xf7, 0x02, 0x00, 0x1f, 0xec, 0x00, 0x00,
+ 0xb2, 0xf8, 0x4d, 0x00, 0xa1, 0xec, 0x00, 0x00, 0x43, 0xf7, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xd8,
+ 0xa0, 0x90, 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x5a,
+ 0xf0, 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x0a, 0xef,
+ 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x97, 0xf0, 0xa0,
+ 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x94, 0xf6, 0xa0, 0x90,
+ 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x95, 0xf5, 0xa0, 0x90, 0x98,
+ 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x34, 0xf7, 0xa0, 0x90,
+ 0x98, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x40,
+ 0xf0, 0x8e, 0xee, 0x40, 0x96, 0x0a, 0x65, 0x00, 0x7d, 0x11, 0x93,
+ 0x76, 0xf7, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x57, 0xee, 0x40,
+ 0xf1, 0x1b, 0xd7, 0x76, 0xf7, 0xc5, 0x92, 0x02, 0x99, 0x41, 0x92,
+ 0xc4, 0xd2, 0x40, 0x92, 0xc4, 0xd2, 0x0f, 0x9f, 0x95, 0xf8, 0x0f,
+ 0x9f, 0x57, 0xee, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0x19, 0xd3, 0x12, 0x95, 0x19,
+ 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, 0xd3, 0x03, 0x82,
+ 0x09, 0x93, 0x65, 0xf7, 0x19, 0xd3, 0x91, 0xec, 0x40, 0xf0, 0x07,
+ 0xf2, 0x40, 0xf0, 0x75, 0xf3, 0x11, 0x93, 0x04, 0xec, 0x42, 0x42,
+ 0x02, 0x5e, 0x0f, 0x9f, 0x8c, 0xee, 0x40, 0x92, 0x19, 0xd3, 0x04,
+ 0xec, 0x40, 0xf0, 0xe0, 0xf1, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff,
+ 0xfd, 0x19, 0xd3, 0x44, 0x96, 0x40, 0xf0, 0x2d, 0xf7, 0x40, 0xf0,
+ 0x6d, 0xee, 0x4b, 0x62, 0x0a, 0x95, 0x2e, 0xee, 0xd1, 0xd4, 0x0b,
+ 0x97, 0x2b, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x00, 0xee, 0xd1, 0xd4,
+ 0x0b, 0x97, 0x2f, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x34, 0xee, 0xd1,
+ 0xd4, 0x0b, 0x97, 0x39, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x3e, 0xee,
+ 0xd1, 0xd4, 0x0b, 0x97, 0x43, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x2e,
+ 0xee, 0xd1, 0xd4, 0x0b, 0x97, 0x48, 0xee, 0xd1, 0xd6, 0x0a, 0x95,
+ 0x49, 0xee, 0xc1, 0xd4, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, 0xc3,
+ 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x43, 0xf1, 0x09, 0x93, 0x01, 0x3f,
+ 0x19, 0xd3, 0xc0, 0x85, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff,
+ 0xfc, 0x19, 0xd3, 0x44, 0x96, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, 0x03, 0x96, 0x41,
+ 0x02, 0x03, 0x99, 0xc4, 0x94, 0x42, 0x04, 0xc1, 0x04, 0xc2, 0x94,
+ 0xc3, 0xd4, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0x13, 0x97, 0x95, 0xec,
+ 0x1b, 0xd7, 0x02, 0x80, 0x11, 0x93, 0x99, 0xec, 0x19, 0xd3, 0x7c,
+ 0x96, 0x0b, 0x97, 0xa0, 0x00, 0x1b, 0xd7, 0x6e, 0xec, 0x0a, 0x65,
+ 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xff, 0xbf, 0x11,
+ 0xa3, 0x9a, 0xec, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xe9,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01,
+ 0x00, 0x14, 0x99, 0x03, 0x80, 0x0c, 0xb3, 0x00, 0x10, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x3d, 0xf0, 0x11, 0x93, 0x9f, 0xec, 0x41,
+ 0x02, 0x19, 0xd3, 0x9f, 0xec, 0x11, 0x93, 0x74, 0xf7, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x2a, 0xef, 0x0a, 0x65, 0xfe, 0x7f, 0x02,
+ 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x04, 0xc2, 0xd2, 0x0f, 0x9f,
+ 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x02, 0xd2, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x76, 0xef, 0x41, 0x92, 0x19, 0xd3, 0x94, 0xec,
+ 0x19, 0xd3, 0x9f, 0xec, 0x12, 0x95, 0x02, 0x80, 0x1a, 0xd5, 0x95,
+ 0xec, 0x13, 0x97, 0x7c, 0x96, 0x1b, 0xd7, 0x99, 0xec, 0x0a, 0x65,
+ 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0x00, 0x40, 0x19,
+ 0xd3, 0x9a, 0xec, 0x09, 0x63, 0x00, 0x40, 0xc2, 0xd2, 0x02, 0x94,
+ 0x1a, 0xd5, 0x7c, 0x96, 0x0c, 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x56, 0xef, 0x0c, 0xb3, 0xff, 0x07, 0x0f, 0x9f,
+ 0x5a, 0xef, 0x11, 0x93, 0x06, 0x80, 0x09, 0xb3, 0xff, 0x07, 0x09,
+ 0x03, 0x00, 0xa0, 0x19, 0xd3, 0x97, 0xec, 0x40, 0x98, 0x0b, 0x97,
+ 0x9c, 0xec, 0x04, 0x95, 0x03, 0x05, 0x14, 0x03, 0x97, 0xec, 0x46,
+ 0x02, 0xc1, 0x92, 0xc2, 0xd2, 0x41, 0x08, 0x42, 0x48, 0x02, 0x9e,
+ 0x0f, 0x9f, 0x61, 0xef, 0x11, 0x93, 0x97, 0xec, 0xc1, 0x92, 0xc5,
+ 0xd2, 0x5f, 0xb2, 0x19, 0xd3, 0x9b, 0xec, 0x0f, 0x9f, 0x79, 0xef,
+ 0x13, 0x97, 0x98, 0xec, 0xc5, 0xd6, 0x11, 0x93, 0x03, 0x80, 0x09,
+ 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x8f, 0xef,
+ 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11,
+ 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x10, 0x19, 0xd3, 0x79, 0xf7,
+ 0x40, 0x98, 0x1c, 0xd9, 0x9b, 0xec, 0x12, 0x95, 0x9b, 0xec, 0x40,
+ 0x44, 0x02, 0x4e, 0x0f, 0x9f, 0x2c, 0xf0, 0x0a, 0xb3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xad, 0xef, 0x0a, 0xb3, 0x07,
+ 0x00, 0x09, 0x05, 0xa9, 0xec, 0xc2, 0x94, 0x01, 0xd4, 0x09, 0x03,
+ 0xa1, 0xec, 0xc1, 0x92, 0x19, 0xd3, 0x9b, 0xec, 0xc5, 0x94, 0x0a,
+ 0xb5, 0x00, 0xff, 0x01, 0xa5, 0xc5, 0xd4, 0x0f, 0x9f, 0xb9, 0xef,
+ 0x0a, 0x05, 0xff, 0xff, 0x0a, 0x03, 0xb1, 0xec, 0xc1, 0x92, 0x01,
+ 0xd2, 0x1a, 0xd5, 0x9b, 0xec, 0xc5, 0x96, 0x0b, 0x07, 0xff, 0xff,
+ 0xc5, 0xd6, 0x11, 0x93, 0x97, 0xec, 0xc5, 0x98, 0xc1, 0xd8, 0x11,
+ 0x93, 0x97, 0xec, 0x09, 0x05, 0x0b, 0x00, 0x03, 0xd4, 0xc2, 0x96,
+ 0x06, 0xd6, 0x7b, 0x95, 0x7a, 0x95, 0x4c, 0x02, 0xc1, 0x92, 0x59,
+ 0x93, 0x59, 0x93, 0x01, 0xa5, 0x01, 0x98, 0x0c, 0xf5, 0x7b, 0x93,
+ 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xb3, 0xff, 0x00, 0x04,
+ 0xd2, 0x5c, 0x93, 0x59, 0x93, 0x04, 0x94, 0x01, 0xa5, 0x03, 0x96,
+ 0xc3, 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4c, 0x02, 0x05, 0xd2, 0xc1,
+ 0x92, 0x09, 0xb3, 0x00, 0xff, 0x7c, 0x95, 0x7a, 0x95, 0x02, 0xa3,
+ 0x05, 0x98, 0xc4, 0xd2, 0x12, 0x95, 0x97, 0xec, 0x45, 0x04, 0x02,
+ 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, 0x12, 0x95,
+ 0x9b, 0xec, 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x01, 0xf0, 0x12, 0x95, 0x97, 0xec, 0x4a, 0x04, 0x02, 0x99,
+ 0xc4, 0x92, 0x01, 0x98, 0x0c, 0xf3, 0x7b, 0x93, 0x41, 0x02, 0x0f,
+ 0x9f, 0x22, 0xf0, 0x43, 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0x23, 0xf0,
+ 0x11, 0x93, 0x97, 0xec, 0x42, 0x02, 0x0a, 0x05, 0xff, 0xff, 0xc1,
+ 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4a, 0x02, 0x12, 0x95, 0x60, 0x96,
+ 0xc1, 0xd4, 0x12, 0x95, 0x97, 0xec, 0x4b, 0x04, 0x02, 0x97, 0xc3,
+ 0x92, 0x09, 0xb3, 0x1f, 0xff, 0xc2, 0xd2, 0x12, 0x95, 0x97, 0xec,
+ 0x4b, 0x04, 0x11, 0x93, 0x62, 0x96, 0x41, 0x93, 0x59, 0x93, 0x02,
+ 0x99, 0xc4, 0xa2, 0xc2, 0xd2, 0xc5, 0x92, 0x19, 0xd3, 0x98, 0xec,
+ 0x0a, 0x95, 0x0c, 0x02, 0x1a, 0xd5, 0x02, 0x80, 0x0f, 0x9f, 0x57,
+ 0xf0, 0x09, 0x63, 0xfe, 0x7f, 0x01, 0x97, 0xc3, 0x94, 0x0a, 0xa5,
+ 0x00, 0x04, 0xc1, 0xd4, 0x11, 0x93, 0x9f, 0xec, 0x09, 0xa3, 0x00,
+ 0x01, 0x19, 0xd3, 0x9f, 0xec, 0x40, 0xf0, 0xdf, 0xee, 0x0f, 0x9f,
+ 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f,
+ 0x9f, 0x4c, 0xf0, 0x40, 0xf0, 0xdf, 0xee, 0x11, 0x93, 0x95, 0xec,
+ 0x44, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x57, 0xf0, 0x48,
+ 0x98, 0x1c, 0xd9, 0x02, 0x80, 0x11, 0x93, 0x91, 0xec, 0x41, 0x22,
+ 0x0a, 0x95, 0x57, 0xf0, 0x88, 0xd4, 0x88, 0xdc, 0x91, 0x9a, 0x47,
+ 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00,
+ 0x11, 0x93, 0x04, 0x82, 0x48, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x6e, 0xf0, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92,
+ 0x09, 0xb3, 0xff, 0xfe, 0xc2, 0xd2, 0x41, 0x92, 0x19, 0xd3, 0xbf,
+ 0xec, 0x11, 0x93, 0x04, 0x82, 0x43, 0xb2, 0x12, 0x95, 0x03, 0x82,
+ 0x02, 0xb3, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf0, 0x0a,
+ 0xb3, 0x00, 0xff, 0x48, 0xa2, 0x19, 0xd3, 0x03, 0x82, 0x40, 0xf0,
+ 0x82, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f,
+ 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x07, 0x82, 0x11, 0x43, 0x03, 0xec,
+ 0x02, 0x0e, 0x0f, 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x03, 0x82, 0x09,
+ 0xa3, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, 0x40, 0x96, 0x1b, 0xd7,
+ 0xbf, 0xec, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x11, 0x93, 0x20, 0xbc, 0xc8, 0xd2, 0x40, 0xf0, 0xe9, 0xf0,
+ 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08,
+ 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, 0x41, 0x02,
+ 0xc1, 0x92, 0x01, 0x97, 0xc3, 0x96, 0xc2, 0xd6, 0x0a, 0x45, 0x00,
+ 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0xc1, 0x92, 0x41, 0xb2,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xe6, 0xf0, 0x11, 0x93, 0xc0,
+ 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0x41, 0x98,
+ 0x1c, 0xd9, 0xc0, 0xec, 0x12, 0x95, 0x02, 0x80, 0x01, 0xd4, 0x40,
+ 0xf0, 0xfe, 0xf1, 0x0b, 0x67, 0xfd, 0x7d, 0x03, 0x99, 0xc4, 0x92,
+ 0x0c, 0x99, 0x96, 0x03, 0x1c, 0xd9, 0x06, 0x82, 0x41, 0x98, 0x1c,
+ 0xd9, 0x02, 0x82, 0x42, 0x98, 0x1c, 0xd9, 0x05, 0x82, 0x0c, 0x69,
+ 0x80, 0x7f, 0x1c, 0xd9, 0x00, 0xb0, 0x09, 0xa3, 0x00, 0x01, 0xc3,
+ 0xd2, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0xe4, 0xf0, 0x42, 0xa4, 0x1a, 0xd5, 0x02, 0x80, 0x42,
+ 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b,
+ 0x01, 0x00, 0x05, 0x92, 0xc5, 0xd2, 0x60, 0xb2, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0xf6, 0xf0, 0x40, 0xf0, 0xd2, 0xf6, 0xc5, 0x94,
+ 0x0a, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xff,
+ 0xf0, 0x40, 0xf0, 0xc0, 0xf5, 0xc5, 0x96, 0x0b, 0xb3, 0x40, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x08, 0xf1, 0x40, 0xf0, 0xfa,
+ 0xf4, 0xc5, 0x94, 0x0a, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0x70, 0xf1, 0x13, 0x97, 0x21, 0xbc, 0x01, 0xd6, 0x0b,
+ 0xb3, 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1a, 0xf1,
+ 0x40, 0xf0, 0x62, 0xfb, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40,
+ 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf1, 0x40, 0xf0, 0x6c, 0xfb,
+ 0x01, 0x96, 0x0b, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x4c, 0xf1, 0x40, 0xf0, 0xb0, 0xfa, 0x41, 0x92, 0x19, 0xd3,
+ 0x73, 0xf7, 0x11, 0x93, 0x03, 0xec, 0x09, 0x43, 0x40, 0x00, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x39, 0xf1, 0x40, 0x94, 0x1a, 0xd5, 0x73, 0xf7,
+ 0x11, 0x93, 0x00, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55,
+ 0xf1, 0x11, 0x93, 0xc1, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x55, 0xf1, 0x40, 0xf0, 0xe0, 0xf1, 0x41, 0x96, 0x1b, 0xd7, 0xc1,
+ 0xec, 0x0f, 0x9f, 0x55, 0xf1, 0x01, 0x94, 0x0a, 0xb3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, 0xf1, 0x40, 0xf0, 0x7c,
+ 0xfb, 0x01, 0x96, 0x0b, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0x5e, 0xf1, 0x40, 0xf0, 0x87, 0xfb, 0x11, 0x93, 0x10,
+ 0xec, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x67, 0xf1, 0x44, 0x92,
+ 0x0f, 0x9f, 0x6b, 0xf1, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x6d,
+ 0xf1, 0x19, 0xd3, 0x0b, 0xbc, 0x40, 0x94, 0x1a, 0xd5, 0x10, 0xec,
+ 0xc5, 0x96, 0x0b, 0xb3, 0x80, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0xba, 0xf1, 0x11, 0x93, 0x28, 0xbc, 0x01, 0xd2, 0x09, 0xb3,
+ 0x40, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x82, 0xf1, 0x40,
+ 0xf0, 0xb5, 0xf6, 0x01, 0x94, 0x0a, 0xb3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf1, 0x40, 0xf0, 0x6d, 0xee, 0x40,
+ 0xf0, 0x8f, 0xfb, 0x40, 0xf0, 0xc3, 0xf1, 0x40, 0x96, 0x1b, 0xd7,
+ 0x00, 0xec, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, 0x0a,
+ 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xb1, 0xf1,
+ 0x40, 0xf0, 0x9e, 0xfb, 0x09, 0x63, 0x00, 0x44, 0x01, 0x97, 0xc3,
+ 0x94, 0x48, 0xa4, 0xc1, 0xd4, 0x00, 0xee, 0x40, 0x92, 0x19, 0xd3,
+ 0x12, 0x95, 0x19, 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19,
+ 0xd3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94,
+ 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xba,
+ 0xf1, 0x40, 0xf0, 0xae, 0xfb, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97,
+ 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3,
+ 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xeb,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09,
+ 0x63, 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xe8, 0x43,
+ 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a,
+ 0x65, 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff,
+ 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09,
+ 0xb3, 0xfb, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x02,
+ 0x80, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00,
+ 0x09, 0x93, 0x00, 0x09, 0x19, 0xd3, 0x02, 0x80, 0x40, 0xf0, 0xfe,
+ 0xf1, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0xc8, 0xd2, 0x09, 0x93,
+ 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0,
+ 0xd8, 0xf4, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x2d, 0xf2, 0x0a,
+ 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2,
+ 0x0f, 0x9f, 0x3a, 0xf2, 0x40, 0xf0, 0x3c, 0xf2, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x3a, 0xf2, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec,
+ 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xf1, 0xbd,
+ 0x19, 0xd3, 0xb6, 0xec, 0x11, 0x93, 0xb4, 0xec, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x54, 0xf2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xc3, 0x94, 0x0a, 0x07, 0x07, 0x00, 0xc1, 0xd6, 0x0a, 0x05, 0x00,
+ 0xa0, 0x1a, 0xd5, 0x96, 0xec, 0x11, 0x93, 0xb6, 0xec, 0x19, 0xd3,
+ 0x01, 0x80, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x41,
+ 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x13, 0x97, 0xb4, 0xec, 0x40,
+ 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xc3, 0xf2, 0x12, 0x95, 0x96, 0xec,
+ 0x0a, 0x03, 0x07, 0x00, 0xc1, 0x92, 0xc2, 0xd2, 0x11, 0x93, 0x96,
+ 0xec, 0x09, 0x05, 0x01, 0x00, 0x48, 0x02, 0xc1, 0x92, 0xc2, 0xd2,
+ 0x11, 0x93, 0x96, 0xec, 0x4e, 0x02, 0xc1, 0x94, 0xc5, 0xd6, 0xc5,
+ 0x92, 0x11, 0x07, 0x96, 0xec, 0x0b, 0x03, 0x0f, 0x00, 0xc1, 0x98,
+ 0x46, 0x06, 0x7a, 0x93, 0x79, 0x93, 0x5c, 0x95, 0x5a, 0x95, 0x02,
+ 0xa3, 0xc3, 0xd2, 0x04, 0x95, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6,
+ 0x42, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0x7d, 0xf2, 0x11, 0x93, 0x96,
+ 0xec, 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xc1, 0x92, 0xc2, 0xd2,
+ 0x11, 0x93, 0x96, 0xec, 0xc1, 0x92, 0x09, 0xb5, 0x1f, 0x00, 0x43,
+ 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0xaa, 0xf2, 0x40, 0x44, 0x02, 0x4e,
+ 0x0f, 0x9f, 0xab, 0xf2, 0x0a, 0x05, 0xff, 0xff, 0x0f, 0x9f, 0xab,
+ 0xf2, 0x43, 0x94, 0x11, 0x93, 0x96, 0xec, 0x42, 0x02, 0xc1, 0xd4,
+ 0x13, 0x97, 0x96, 0xec, 0x03, 0x93, 0xd1, 0x94, 0x7a, 0x95, 0x7a,
+ 0x95, 0xc1, 0x92, 0x59, 0x93, 0x59, 0x93, 0x01, 0x05, 0x49, 0x06,
+ 0xc3, 0x92, 0x7f, 0xb2, 0x01, 0x05, 0x1a, 0xd5, 0xb4, 0xec, 0x0a,
+ 0x05, 0xf2, 0xff, 0x1a, 0xd5, 0x92, 0xec, 0x11, 0x93, 0x92, 0xec,
+ 0x12, 0x95, 0xb6, 0xec, 0x02, 0x43, 0x02, 0x8e, 0x0f, 0x9f, 0x11,
+ 0xf3, 0x02, 0x0e, 0x0f, 0x9f, 0xe4, 0xf2, 0x11, 0x93, 0x7a, 0xf7,
+ 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09,
+ 0xa3, 0x80, 0x00, 0x19, 0xd3, 0x79, 0xf7, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x95, 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, 0x40, 0x96, 0x1b,
+ 0xd7, 0xb4, 0xec, 0x0f, 0x9f, 0x29, 0xf3, 0x11, 0x93, 0x03, 0x80,
+ 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xf6,
+ 0xf2, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0xf6, 0xf2, 0x40, 0xf0, 0x3d, 0xf3, 0x0f, 0x9f, 0x2b, 0xf3, 0x41,
+ 0x92, 0xc8, 0xd2, 0x0a, 0x95, 0x91, 0xec, 0xc8, 0xd4, 0x40, 0xf0,
+ 0xd0, 0xee, 0x42, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0x09, 0xf3, 0x42, 0x96, 0x1b, 0xd7, 0xc0, 0xec,
+ 0x0f, 0x9f, 0x2b, 0xf3, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3,
+ 0x92, 0x42, 0xa2, 0xc2, 0xd2, 0x0f, 0x9f, 0x2b, 0xf3, 0x12, 0x45,
+ 0x03, 0xec, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf3, 0x11, 0x93, 0x7a,
+ 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7,
+ 0x09, 0xa3, 0x00, 0x08, 0x19, 0xd3, 0x79, 0xf7, 0x1a, 0xd5, 0x92,
+ 0xec, 0x11, 0x93, 0x92, 0xec, 0x19, 0x25, 0x92, 0xec, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x41, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0x3d, 0xf3,
+ 0x40, 0x92, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40,
+ 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x75, 0xf7, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0x4d, 0xf3, 0x0a, 0x65, 0xbc, 0x69, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0x83, 0x00, 0x02, 0xc2, 0xd2, 0x11, 0x93, 0x03,
+ 0x80, 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x60, 0xf3, 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a,
+ 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x20, 0x19, 0xd3,
+ 0x79, 0xf7, 0x11, 0x93, 0xb5, 0xec, 0x19, 0xd3, 0x04, 0x80, 0x12,
+ 0x95, 0xb4, 0xec, 0x1a, 0xd5, 0x05, 0x80, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x97, 0xc3, 0x96, 0x1b, 0xd7, 0xb5, 0xec, 0x40, 0x94, 0x1a,
+ 0xd5, 0xb4, 0xec, 0x19, 0xd3, 0xf2, 0xbd, 0x88, 0x98, 0x90, 0x9a,
+ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19,
+ 0xd3, 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01,
+ 0x00, 0x11, 0x93, 0x01, 0x82, 0xc5, 0xd2, 0x40, 0x94, 0x01, 0xd4,
+ 0x13, 0x97, 0xb8, 0xec, 0x02, 0xd6, 0x03, 0x95, 0x0c, 0x99, 0xbb,
+ 0xec, 0x04, 0x05, 0x13, 0x97, 0x03, 0xec, 0x01, 0x27, 0x02, 0x99,
+ 0xc4, 0x92, 0x03, 0x03, 0xc2, 0xd2, 0x14, 0x99, 0xba, 0xec, 0x03,
+ 0x09, 0x1c, 0xd9, 0xba, 0xec, 0x12, 0x95, 0x04, 0x82, 0x0a, 0xb3,
+ 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xc6, 0xf4, 0x01,
+ 0x92, 0x03, 0xd2, 0x0a, 0xa3, 0x02, 0x00, 0x19, 0xd3, 0x04, 0x82,
+ 0x02, 0x96, 0x0b, 0x05, 0x01, 0x00, 0x1a, 0xd5, 0xb8, 0xec, 0xc5,
+ 0x92, 0x43, 0x42, 0x02, 0x9e, 0x0f, 0x9f, 0xce, 0xf3, 0x42, 0x44,
+ 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x40,
+ 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xce, 0xf3, 0x0c, 0x49, 0xd3, 0x08,
+ 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x63, 0x07, 0x82, 0x11,
+ 0xa3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, 0x79, 0x93, 0x79, 0x93,
+ 0x03, 0xd2, 0xc5, 0x94, 0x0a, 0xb5, 0xfc, 0xff, 0x04, 0xd4, 0x03,
+ 0x96, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xdd, 0xf3, 0x11, 0x93,
+ 0xb8, 0xec, 0x41, 0x42, 0x02, 0x8e, 0x0f, 0x9f, 0xe4, 0xf3, 0xc5,
+ 0x98, 0x0c, 0x03, 0xff, 0xff, 0x42, 0x42, 0x02, 0x8e, 0x0f, 0x9f,
+ 0x0b, 0xf4, 0x0a, 0x95, 0xbb, 0xec, 0x42, 0x92, 0x19, 0xd3, 0xb9,
+ 0xec, 0xc5, 0x96, 0x43, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0xfd, 0xf3,
+ 0x0b, 0x07, 0xfc, 0xff, 0xc5, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xc8,
+ 0xbc, 0xd2, 0x96, 0x1b, 0xd7, 0xca, 0xbc, 0x09, 0x03, 0xff, 0xff,
+ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe9, 0xf3, 0x19, 0xd3, 0xb9,
+ 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x09, 0xf4, 0x0a, 0x05,
+ 0xfe, 0xff, 0xca, 0xd2, 0xc2, 0xd2, 0x0f, 0x9f, 0x0b, 0xf4, 0x1a,
+ 0xd5, 0x93, 0xec, 0x03, 0x98, 0x40, 0x48, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x38, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x9e, 0x0f,
+ 0x9f, 0x1b, 0xf4, 0x04, 0x94, 0x48, 0x44, 0x02, 0x4e, 0x0f, 0x9f,
+ 0x26, 0xf4, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x38, 0xf4, 0x11,
+ 0x93, 0x04, 0x82, 0x41, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f,
+ 0x38, 0xf4, 0x41, 0x96, 0x01, 0xd6, 0x0a, 0x65, 0xbd, 0x43, 0x02,
+ 0x99, 0xc4, 0x92, 0x09, 0xa3, 0x80, 0x00, 0xc2, 0xd2, 0x0a, 0x65,
+ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2,
+ 0xd2, 0x0f, 0x9f, 0x97, 0xf4, 0xc5, 0x98, 0x43, 0x48, 0x02, 0x9e,
+ 0x0f, 0x9f, 0x97, 0xf4, 0x4f, 0x96, 0x0c, 0xb3, 0x01, 0x00, 0x40,
+ 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x45, 0xf4, 0x47, 0x96, 0x11, 0x93,
+ 0xb7, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x73, 0xf4, 0x11,
+ 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x73, 0xf4,
+ 0x12, 0x95, 0x00, 0x82, 0x0a, 0x05, 0xff, 0xaf, 0x05, 0xd4, 0xc8,
+ 0xd6, 0xc8, 0xd2, 0x40, 0xf0, 0x18, 0xf7, 0x42, 0x00, 0x05, 0x96,
+ 0xc3, 0x94, 0x01, 0xb5, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x68,
+ 0xf4, 0x11, 0x93, 0xba, 0xec, 0x4d, 0x42, 0x02, 0x8e, 0x0f, 0x9f,
+ 0x73, 0xf4, 0x06, 0x98, 0x50, 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40,
+ 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x92, 0x03, 0xd2, 0x0f, 0x9f,
+ 0x9c, 0xf4, 0x03, 0x94, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x80,
+ 0xf4, 0x0a, 0x65, 0x5e, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x48, 0xa2,
+ 0xc2, 0xd2, 0x0f, 0x9f, 0x9c, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x0c,
+ 0x99, 0xbb, 0xec, 0x04, 0x03, 0x04, 0x96, 0x13, 0x25, 0x03, 0xec,
+ 0xc1, 0xd4, 0x11, 0x93, 0xba, 0xec, 0x19, 0x05, 0xba, 0xec, 0x1b,
+ 0xd7, 0x01, 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x99, 0xc4, 0x92,
+ 0x43, 0xa2, 0xc2, 0xd2, 0x41, 0x92, 0x01, 0xd2, 0x03, 0x94, 0x40,
+ 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x11, 0x93, 0xb9, 0xec,
+ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa8, 0xf4, 0x19, 0xd3, 0xb8,
+ 0xec, 0x19, 0xd3, 0xba, 0xec, 0x19, 0xd3, 0xbb, 0xec, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x41, 0x98, 0x1c,
+ 0xd9, 0xb7, 0xec, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e,
+ 0x0f, 0x9f, 0xc1, 0xf4, 0x11, 0x93, 0x00, 0x82, 0x19, 0xd3, 0x02,
+ 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3,
+ 0x00, 0x01, 0xc2, 0xd2, 0x40, 0x98, 0x1c, 0xd9, 0xbf, 0xec, 0x0f,
+ 0x9f, 0xc9, 0xf4, 0x01, 0x92, 0x19, 0xd3, 0xb7, 0xec, 0x01, 0x94,
+ 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xd5, 0xf4, 0x0a, 0x65, 0xea,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2,
+ 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x12, 0x95, 0x03, 0x80, 0x0a, 0xb3, 0x00, 0x40, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0xf4, 0xf4, 0x0a, 0xb7, 0x00, 0x08, 0x40,
+ 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xf7, 0xf4, 0x11, 0x93, 0x03, 0xec,
+ 0x41, 0x02, 0x09, 0xb3, 0xfe, 0xff, 0x12, 0x95, 0x07, 0x80, 0x01,
+ 0x45, 0x02, 0x8e, 0x0f, 0x9f, 0xf7, 0xf4, 0x41, 0x92, 0x0f, 0x9f,
+ 0xf8, 0xf4, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x41,
+ 0x20, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x13, 0x97, 0x6e,
+ 0xec, 0x0b, 0x47, 0xa0, 0x00, 0x02, 0x5e, 0x0f, 0x9f, 0x23, 0xf5,
+ 0x09, 0x63, 0x08, 0x43, 0x0a, 0x65, 0xff, 0x5f, 0x01, 0x99, 0xc4,
+ 0xd4, 0x0a, 0x95, 0x9b, 0xec, 0xd2, 0x96, 0x1b, 0xd7, 0xfa, 0xbc,
+ 0xd2, 0x96, 0xc4, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0xd2,
+ 0x96, 0xc1, 0xd6, 0xc2, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x0f, 0x9f,
+ 0x61, 0xf5, 0x0c, 0x69, 0xff, 0x6f, 0x1c, 0xd9, 0xf8, 0xbc, 0x0b,
+ 0x47, 0x10, 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0x3b, 0xf5, 0x0a, 0x95,
+ 0x6f, 0xec, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, 0xc4, 0xd6, 0xd2,
+ 0x96, 0x1b, 0xd7, 0xf8, 0xbc, 0x0c, 0x69, 0xee, 0x6a, 0xc1, 0xd8,
+ 0xc2, 0x94, 0x1a, 0xd5, 0xf8, 0xbc, 0x40, 0x92, 0xc5, 0xd2, 0x11,
+ 0x43, 0xc2, 0xec, 0x02, 0x0e, 0x0f, 0x9f, 0x5e, 0xf5, 0xc5, 0x94,
+ 0x0a, 0x03, 0x71, 0xec, 0xc1, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x11,
+ 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x50, 0xf5,
+ 0x0a, 0x95, 0x6f, 0xec, 0xc8, 0xd4, 0x40, 0xf0, 0x39, 0xf7, 0x19,
+ 0xd3, 0xf8, 0xbc, 0x41, 0x00, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6,
+ 0x13, 0x47, 0xc2, 0xec, 0x02, 0x1e, 0x0f, 0x9f, 0x42, 0xf5, 0x40,
+ 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0x40, 0x92, 0x19, 0xd3, 0x6e, 0xec,
+ 0x19, 0xd3, 0xc2, 0xec, 0x0a, 0x65, 0x52, 0x43, 0x02, 0x97, 0xc3,
+ 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x99,
+ 0xc4, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x41, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x43, 0x20, 0x08, 0x0b, 0x01, 0x00,
+ 0x06, 0x92, 0x01, 0xd2, 0x0a, 0x65, 0xf0, 0x6a, 0x0b, 0x97, 0x6f,
+ 0xec, 0x02, 0x99, 0xc4, 0x98, 0xd3, 0xd8, 0x02, 0xd6, 0x0a, 0x03,
+ 0x02, 0x00, 0x01, 0x97, 0xc3, 0x98, 0x02, 0x96, 0xc3, 0xd8, 0x01,
+ 0x96, 0xc1, 0xd6, 0x1a, 0xd5, 0x6e, 0xec, 0xc5, 0x98, 0x14, 0x99,
+ 0x6f, 0xec, 0xc2, 0xd8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88,
+ 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0xc8, 0xd2, 0x40, 0xf0,
+ 0x76, 0xf5, 0x41, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0xb0, 0xf5, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0xad, 0xf5, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x42,
+ 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0xc0, 0xec, 0x0a, 0x65,
+ 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2,
+ 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3,
+ 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x63,
+ 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xaf, 0xbc, 0x47, 0xb2,
+ 0x59, 0x95, 0x5a, 0x95, 0x12, 0xa5, 0xbf, 0xbc, 0x0a, 0xb3, 0x01,
+ 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xd2, 0xf5, 0x41, 0x04,
+ 0x05, 0x93, 0x40, 0x96, 0x20, 0xd6, 0x62, 0x97, 0x0f, 0x9f, 0xe1,
+ 0xf5, 0x14, 0x99, 0xfc, 0xbc, 0xd1, 0xd8, 0x14, 0x99, 0xfe, 0xbc,
+ 0xd1, 0xd8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x20, 0x98, 0x03,
+ 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0xd8, 0xf5, 0xc5, 0x92, 0x62, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0xfa, 0xf5, 0x02, 0x8e, 0x0f, 0x9f, 0xf4,
+ 0xf5, 0x61, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1e, 0xf6, 0x0f, 0x9f,
+ 0x4b, 0xf6, 0x63, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x41, 0xf6, 0x0f,
+ 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x0c, 0x99, 0x71, 0xec,
+ 0x0b, 0x05, 0xff, 0xff, 0x40, 0x96, 0x0f, 0x9f, 0x07, 0xf6, 0xd1,
+ 0x96, 0xd4, 0xd6, 0x20, 0x96, 0x41, 0x06, 0x20, 0xd6, 0x02, 0x47,
+ 0x02, 0x1e, 0x0f, 0x9f, 0x03, 0xf6, 0x1a, 0xd5, 0xc2, 0xec, 0x0a,
+ 0x65, 0xeb, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xa3, 0xc0, 0x00,
+ 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09,
+ 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, 0x4b, 0xf6, 0x0a, 0x03,
+ 0xfe, 0xff, 0x61, 0x95, 0x40, 0x98, 0x20, 0xd8, 0x02, 0x49, 0x02,
+ 0x0e, 0x0f, 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x21, 0xd2,
+ 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, 0xc8, 0xd2, 0x21, 0x96, 0xc3,
+ 0x92, 0x42, 0x06, 0x21, 0xd6, 0xc8, 0xd2, 0x22, 0xd4, 0x40, 0xf0,
+ 0xa2, 0xf0, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x22,
+ 0x94, 0x02, 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0x2a, 0xf6, 0x0f, 0x9f,
+ 0x4b, 0xf6, 0x0d, 0x03, 0x03, 0x00, 0xc8, 0xd2, 0x02, 0x92, 0xc8,
+ 0xd2, 0x01, 0x96, 0xc8, 0xd6, 0x40, 0xf0, 0x4e, 0xf6, 0x43, 0x00,
+ 0x63, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x45, 0x20, 0x08,
+ 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x08, 0x00, 0x08, 0x94, 0xc5, 0xd4,
+ 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x03, 0xd4, 0x42, 0x02, 0xc1,
+ 0x92, 0x01, 0xd2, 0x02, 0x97, 0xc5, 0x94, 0x0a, 0x83, 0xff, 0xff,
+ 0x11, 0xb3, 0x2c, 0x93, 0x09, 0xb3, 0xfb, 0xff, 0x19, 0xd3, 0x2c,
+ 0x93, 0x03, 0x92, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x81, 0xf6,
+ 0x01, 0x94, 0xd2, 0x92, 0x19, 0xd3, 0x2c, 0x93, 0x01, 0xd4, 0x02,
+ 0x94, 0x12, 0x95, 0x2c, 0x93, 0x44, 0xa4, 0x1a, 0xd5, 0x2c, 0x93,
+ 0x0a, 0xb5, 0xfb, 0xff, 0x1a, 0xd5, 0x2c, 0x93, 0x0b, 0x07, 0xff,
+ 0xff, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x6c, 0xf6, 0x09, 0x63,
+ 0xd4, 0x6c, 0x01, 0x95, 0xc2, 0x96, 0xc5, 0x94, 0x02, 0xa7, 0xc1,
+ 0xd6, 0x03, 0x92, 0x54, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x91, 0xf6,
+ 0x0a, 0x83, 0xff, 0xff, 0x1b, 0xb3, 0x2c, 0x93, 0x45, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63,
+ 0x00, 0x40, 0x19, 0xd3, 0xf2, 0xbd, 0x40, 0xf0, 0xd8, 0xf4, 0x40,
+ 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa5, 0xf6, 0x40, 0xf0, 0x3c, 0xf2,
+ 0x0f, 0x9f, 0xb3, 0xf6, 0x40, 0x96, 0xc8, 0xd6, 0x09, 0x93, 0x91,
+ 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x0a, 0x65, 0xfe, 0x7f,
+ 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65,
+ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2,
+ 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3,
+ 0xfb, 0xff, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0x2d, 0xbc, 0x0a,
+ 0x65, 0xd8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff,
+ 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x09, 0x63, 0xea, 0x43, 0x01, 0x97, 0xc3, 0x94, 0x44, 0xa4,
+ 0xc1, 0xd4, 0x11, 0x93, 0xb9, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x0c, 0xf7, 0x12, 0x95, 0x93, 0xec, 0x0b, 0x67, 0x36, 0x43,
+ 0xd2, 0x98, 0x1c, 0xd9, 0xc8, 0xbc, 0xd2, 0x98, 0x03, 0x93, 0xc1,
+ 0xd8, 0x11, 0x93, 0xb9, 0xec, 0x09, 0x03, 0xff, 0xff, 0x19, 0xd3,
+ 0xb9, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe5, 0xf6, 0x19,
+ 0xd3, 0xb8, 0xec, 0x19, 0xd3, 0xba, 0xec, 0x0a, 0x05, 0xfe, 0xff,
+ 0xca, 0xd2, 0xca, 0xd2, 0xc2, 0xd2, 0x0a, 0x65, 0x5e, 0x43, 0x02,
+ 0x97, 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43,
+ 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0x0f, 0x9f, 0x15,
+ 0xf7, 0x11, 0x93, 0x03, 0xec, 0x19, 0xd3, 0x01, 0x82, 0x0a, 0x65,
+ 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x43, 0xa2, 0xc2, 0xd2, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x92,
+ 0x04, 0x96, 0x0d, 0x5e, 0x50, 0x46, 0x02, 0x0e, 0x40, 0x92, 0x09,
+ 0xee, 0x44, 0x46, 0x04, 0x0e, 0x59, 0x93, 0x44, 0x26, 0x04, 0x5e,
+ 0x46, 0xee, 0x41, 0x93, 0x41, 0x26, 0x43, 0x4e, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0xb1, 0xfe,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x94,
+ 0x1a, 0xd5, 0x40, 0xf7, 0x11, 0x93, 0x00, 0x90, 0x88, 0x98, 0x90,
+ 0x9a, 0x1d, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x03, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00,
+ 0x21, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf2, 0x6b, 0xf7, 0x00, 0x00,
+ 0x1c, 0xf2, 0x6b, 0xf7, 0x00, 0x00, 0x61, 0xf2, 0x68, 0xf7, 0x6f,
+ 0xf7, 0x00, 0x00, 0x2e, 0xf3, 0x6b, 0xf7, 0x25, 0x47, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/sys/legacy/dev/usb/if_zydreg.h b/sys/legacy/dev/usb/if_zydreg.h
new file mode 100644
index 0000000..feae22f
--- /dev/null
+++ b/sys/legacy/dev/usb/if_zydreg.h
@@ -0,0 +1,1322 @@
+/* $OpenBSD: if_zydreg.h,v 1.19 2006/11/30 19:28:07 damien Exp $ */
+/* $NetBSD: if_zydreg.h,v 1.2 2007/06/16 11:18:45 kiyohara Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * ZyDAS ZD1211/ZD1211B USB WLAN driver.
+ */
+
+#define ZYD_CR_GPI_EN 0x9418
+#define ZYD_CR_RADIO_PD 0x942c
+#define ZYD_CR_RF2948_PD 0x942c
+#define ZYD_CR_EN_PS_MANUAL_AGC 0x943c
+#define ZYD_CR_CONFIG_PHILIPS 0x9440
+#define ZYD_CR_I2C_WRITE 0x9444
+#define ZYD_CR_SA2400_SER_RP 0x9448
+#define ZYD_CR_RADIO_PE 0x9458
+#define ZYD_CR_RST_BUS_MASTER 0x945c
+#define ZYD_CR_RFCFG 0x9464
+#define ZYD_CR_HSTSCHG 0x946c
+#define ZYD_CR_PHY_ON 0x9474
+#define ZYD_CR_RX_DELAY 0x9478
+#define ZYD_CR_RX_PE_DELAY 0x947c
+#define ZYD_CR_GPIO_1 0x9490
+#define ZYD_CR_GPIO_2 0x9494
+#define ZYD_CR_EnZYD_CRyBufMux 0x94a8
+#define ZYD_CR_PS_CTRL 0x9500
+#define ZYD_CR_ADDA_PWR_DWN 0x9504
+#define ZYD_CR_ADDA_MBIAS_WT 0x9508
+#define ZYD_CR_INTERRUPT 0x9510
+#define ZYD_CR_MAC_PS_STATE 0x950c
+#define ZYD_CR_ATIM_WND_PERIOD 0x951c
+#define ZYD_CR_BCN_INTERVAL 0x9520
+#define ZYD_CR_PRE_TBTT 0x9524
+
+/*
+ * MAC registers.
+ */
+#define ZYD_MAC_MACADRL 0x9610 /* MAC address (low) */
+#define ZYD_MAC_MACADRH 0x9614 /* MAC address (high) */
+#define ZYD_MAC_BSSADRL 0x9618 /* BSS address (low) */
+#define ZYD_MAC_BSSADRH 0x961c /* BSS address (high) */
+#define ZYD_MAC_BCNCFG 0x9620 /* BCN configuration */
+#define ZYD_MAC_GHTBL 0x9624 /* Group hash table (low) */
+#define ZYD_MAC_GHTBH 0x9628 /* Group hash table (high) */
+#define ZYD_MAC_RX_TIMEOUT 0x962c /* Rx timeout value */
+#define ZYD_MAC_BAS_RATE 0x9630 /* Basic rate setting */
+#define ZYD_MAC_MAN_RATE 0x9634 /* Mandatory rate setting */
+#define ZYD_MAC_RTSCTSRATE 0x9638 /* RTS CTS rate */
+#define ZYD_MAC_BACKOFF_PROTECT 0x963c /* Backoff protection */
+#define ZYD_MAC_RX_THRESHOLD 0x9640 /* Rx threshold */
+#define ZYD_MAC_TX_PE_CONTROL 0x9644 /* Tx_PE control */
+#define ZYD_MAC_AFTER_PNP 0x9648 /* After PnP */
+#define ZYD_MAC_RX_PE_DELAY 0x964c /* Rx_pe delay */
+#define ZYD_MAC_RX_ADDR2_L 0x9650 /* RX address2 (low) */
+#define ZYD_MAC_RX_ADDR2_H 0x9654 /* RX address2 (high) */
+#define ZYD_MAC_SIFS_ACK_TIME 0x9658 /* Dynamic SIFS ack time */
+#define ZYD_MAC_PHY_DELAY 0x9660 /* PHY delay */
+#define ZYD_MAC_PHY_DELAY2 0x966c /* PHY delay */
+#define ZYD_MAC_BCNFIFO 0x9670 /* Beacon FIFO I/O port */
+#define ZYD_MAC_SNIFFER 0x9674 /* Sniffer on/off */
+#define ZYD_MAC_ENCRYPTION_TYPE 0x9678 /* Encryption type */
+#define ZYD_MAC_RETRY 0x967c /* Retry time */
+#define ZYD_MAC_MISC 0x9680 /* Misc */
+#define ZYD_MAC_STMACHINESTAT 0x9684 /* State machine status */
+#define ZYD_MAC_TX_UNDERRUN_CNT 0x9688 /* TX underrun counter */
+#define ZYD_MAC_RXFILTER 0x968c /* Send to host settings */
+#define ZYD_MAC_ACK_EXT 0x9690 /* Acknowledge extension */
+#define ZYD_MAC_BCNFIFOST 0x9694 /* BCN FIFO set and status */
+#define ZYD_MAC_DIFS_EIFS_SIFS 0x9698 /* DIFS, EIFS & SIFS settings */
+#define ZYD_MAC_RX_TIMEOUT_CNT 0x969c /* RX timeout count */
+#define ZYD_MAC_RX_TOTAL_FRAME 0x96a0 /* RX total frame count */
+#define ZYD_MAC_RX_CRC32_CNT 0x96a4 /* RX CRC32 frame count */
+#define ZYD_MAC_RX_CRC16_CNT 0x96a8 /* RX CRC16 frame count */
+#define ZYD_MAC_RX_UDEC 0x96ac /* RX unicast decr. error count */
+#define ZYD_MAC_RX_OVERRUN_CNT 0x96b0 /* RX FIFO overrun count */
+#define ZYD_MAC_RX_MDEC 0x96bc /* RX multicast decr. err. cnt. */
+#define ZYD_MAC_NAV_TCR 0x96c4 /* NAV timer count read */
+#define ZYD_MAC_BACKOFF_ST_RD 0x96c8 /* Backoff status read */
+#define ZYD_MAC_DM_RETRY_CNT_RD 0x96cc /* DM retry count read */
+#define ZYD_MAC_RX_ACR 0x96d0 /* RX arbitration count read */
+#define ZYD_MAC_TX_CCR 0x96d4 /* Tx complete count read */
+#define ZYD_MAC_TCB_ADDR 0x96e8 /* Current PCI process TCP addr */
+#define ZYD_MAC_RCB_ADDR 0x96ec /* Next RCB address */
+#define ZYD_MAC_CONT_WIN_LIMIT 0x96f0 /* Contention window limit */
+#define ZYD_MAC_TX_PKT 0x96f4 /* Tx total packet count read */
+#define ZYD_MAC_DL_CTRL 0x96f8 /* Download control */
+#define ZYD_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */
+#define ZYD_MACB_TXPWR_CTL1 0x9b00
+#define ZYD_MACB_TXPWR_CTL2 0x9b04
+#define ZYD_MACB_TXPWR_CTL3 0x9b08
+#define ZYD_MACB_TXPWR_CTL4 0x9b0c
+#define ZYD_MACB_AIFS_CTL1 0x9b10
+#define ZYD_MACB_AIFS_CTL2 0x9b14
+#define ZYD_MACB_TXOP 0x9b20
+#define ZYD_MACB_MAX_RETRY 0x9b28
+
+/*
+ * Miscellanous registers.
+ */
+#define ZYD_FIRMWARE_START_ADDR 0xee00
+#define ZYD_FIRMWARE_BASE_ADDR 0xee1d /* Firmware base address */
+
+/*
+ * EEPROM registers.
+ */
+#define ZYD_EEPROM_START_HEAD 0xf800 /* EEPROM start */
+#define ZYD_EEPROM_SUBID 0xf817
+#define ZYD_EEPROM_POD 0xf819
+#define ZYD_EEPROM_MAC_ADDR_P1 0xf81b /* Part 1 of the MAC address */
+#define ZYD_EEPROM_MAC_ADDR_P2 0xf81d /* Part 2 of the MAC address */
+#define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */
+#define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */
+#define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */
+#define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */
+#define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */
+#define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */
+#define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */
+#define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */
+#define ZYD_EEPROM_48M_INT 0xf857 /* Interpolation */
+#define ZYD_EEPROM_54M_CAL 0xf85f /* Calibration */
+#define ZYD_EEPROM_54M_INT 0xf867 /* Interpolation */
+
+/*
+ * Firmware registers offsets (relative to fwbase).
+ */
+#define ZYD_FW_FIRMWARE_REV 0x0000 /* Firmware version */
+#define ZYD_FW_USB_SPEED 0x0001 /* USB speed (!=0 if highspeed) */
+#define ZYD_FW_FIX_TX_RATE 0x0002 /* Fixed TX rate */
+#define ZYD_FW_LINK_STATUS 0x0003
+#define ZYD_FW_SOFT_RESET 0x0004
+#define ZYD_FW_FLASH_CHK 0x0005
+
+/* possible flags for register ZYD_FW_LINK_STATUS */
+#define ZYD_LED1 (1 << 8)
+#define ZYD_LED2 (1 << 9)
+
+/*
+ * RF IDs.
+ */
+#define ZYD_RF_UW2451 0x2 /* not supported yet */
+#define ZYD_RF_UCHIP 0x3 /* not supported yet */
+#define ZYD_RF_AL2230 0x4
+#define ZYD_RF_AL7230B 0x5
+#define ZYD_RF_THETA 0x6 /* not supported yet */
+#define ZYD_RF_AL2210 0x7
+#define ZYD_RF_MAXIM_NEW 0x8
+#define ZYD_RF_GCT 0x9
+#define ZYD_RF_AL2230S 0xa /* not supported yet */
+#define ZYD_RF_RALINK 0xb /* not supported yet */
+#define ZYD_RF_INTERSIL 0xc /* not supported yet */
+#define ZYD_RF_RFMD 0xd
+#define ZYD_RF_MAXIM_NEW2 0xe
+#define ZYD_RF_PHILIPS 0xf /* not supported yet */
+
+/*
+ * PHY registers (8 bits, not documented).
+ */
+#define ZYD_CR0 0x9000
+#define ZYD_CR1 0x9004
+#define ZYD_CR2 0x9008
+#define ZYD_CR3 0x900c
+#define ZYD_CR5 0x9010
+#define ZYD_CR6 0x9014
+#define ZYD_CR7 0x9018
+#define ZYD_CR8 0x901c
+#define ZYD_CR4 0x9020
+#define ZYD_CR9 0x9024
+#define ZYD_CR10 0x9028
+#define ZYD_CR11 0x902c
+#define ZYD_CR12 0x9030
+#define ZYD_CR13 0x9034
+#define ZYD_CR14 0x9038
+#define ZYD_CR15 0x903c
+#define ZYD_CR16 0x9040
+#define ZYD_CR17 0x9044
+#define ZYD_CR18 0x9048
+#define ZYD_CR19 0x904c
+#define ZYD_CR20 0x9050
+#define ZYD_CR21 0x9054
+#define ZYD_CR22 0x9058
+#define ZYD_CR23 0x905c
+#define ZYD_CR24 0x9060
+#define ZYD_CR25 0x9064
+#define ZYD_CR26 0x9068
+#define ZYD_CR27 0x906c
+#define ZYD_CR28 0x9070
+#define ZYD_CR29 0x9074
+#define ZYD_CR30 0x9078
+#define ZYD_CR31 0x907c
+#define ZYD_CR32 0x9080
+#define ZYD_CR33 0x9084
+#define ZYD_CR34 0x9088
+#define ZYD_CR35 0x908c
+#define ZYD_CR36 0x9090
+#define ZYD_CR37 0x9094
+#define ZYD_CR38 0x9098
+#define ZYD_CR39 0x909c
+#define ZYD_CR40 0x90a0
+#define ZYD_CR41 0x90a4
+#define ZYD_CR42 0x90a8
+#define ZYD_CR43 0x90ac
+#define ZYD_CR44 0x90b0
+#define ZYD_CR45 0x90b4
+#define ZYD_CR46 0x90b8
+#define ZYD_CR47 0x90bc
+#define ZYD_CR48 0x90c0
+#define ZYD_CR49 0x90c4
+#define ZYD_CR50 0x90c8
+#define ZYD_CR51 0x90cc
+#define ZYD_CR52 0x90d0
+#define ZYD_CR53 0x90d4
+#define ZYD_CR54 0x90d8
+#define ZYD_CR55 0x90dc
+#define ZYD_CR56 0x90e0
+#define ZYD_CR57 0x90e4
+#define ZYD_CR58 0x90e8
+#define ZYD_CR59 0x90ec
+#define ZYD_CR60 0x90f0
+#define ZYD_CR61 0x90f4
+#define ZYD_CR62 0x90f8
+#define ZYD_CR63 0x90fc
+#define ZYD_CR64 0x9100
+#define ZYD_CR65 0x9104
+#define ZYD_CR66 0x9108
+#define ZYD_CR67 0x910c
+#define ZYD_CR68 0x9110
+#define ZYD_CR69 0x9114
+#define ZYD_CR70 0x9118
+#define ZYD_CR71 0x911c
+#define ZYD_CR72 0x9120
+#define ZYD_CR73 0x9124
+#define ZYD_CR74 0x9128
+#define ZYD_CR75 0x912c
+#define ZYD_CR76 0x9130
+#define ZYD_CR77 0x9134
+#define ZYD_CR78 0x9138
+#define ZYD_CR79 0x913c
+#define ZYD_CR80 0x9140
+#define ZYD_CR81 0x9144
+#define ZYD_CR82 0x9148
+#define ZYD_CR83 0x914c
+#define ZYD_CR84 0x9150
+#define ZYD_CR85 0x9154
+#define ZYD_CR86 0x9158
+#define ZYD_CR87 0x915c
+#define ZYD_CR88 0x9160
+#define ZYD_CR89 0x9164
+#define ZYD_CR90 0x9168
+#define ZYD_CR91 0x916c
+#define ZYD_CR92 0x9170
+#define ZYD_CR93 0x9174
+#define ZYD_CR94 0x9178
+#define ZYD_CR95 0x917c
+#define ZYD_CR96 0x9180
+#define ZYD_CR97 0x9184
+#define ZYD_CR98 0x9188
+#define ZYD_CR99 0x918c
+#define ZYD_CR100 0x9190
+#define ZYD_CR101 0x9194
+#define ZYD_CR102 0x9198
+#define ZYD_CR103 0x919c
+#define ZYD_CR104 0x91a0
+#define ZYD_CR105 0x91a4
+#define ZYD_CR106 0x91a8
+#define ZYD_CR107 0x91ac
+#define ZYD_CR108 0x91b0
+#define ZYD_CR109 0x91b4
+#define ZYD_CR110 0x91b8
+#define ZYD_CR111 0x91bc
+#define ZYD_CR112 0x91c0
+#define ZYD_CR113 0x91c4
+#define ZYD_CR114 0x91c8
+#define ZYD_CR115 0x91cc
+#define ZYD_CR116 0x91d0
+#define ZYD_CR117 0x91d4
+#define ZYD_CR118 0x91d8
+#define ZYD_CR119 0x91dc
+#define ZYD_CR120 0x91e0
+#define ZYD_CR121 0x91e4
+#define ZYD_CR122 0x91e8
+#define ZYD_CR123 0x91ec
+#define ZYD_CR124 0x91f0
+#define ZYD_CR125 0x91f4
+#define ZYD_CR126 0x91f8
+#define ZYD_CR127 0x91fc
+#define ZYD_CR128 0x9200
+#define ZYD_CR129 0x9204
+#define ZYD_CR130 0x9208
+#define ZYD_CR131 0x920c
+#define ZYD_CR132 0x9210
+#define ZYD_CR133 0x9214
+#define ZYD_CR134 0x9218
+#define ZYD_CR135 0x921c
+#define ZYD_CR136 0x9220
+#define ZYD_CR137 0x9224
+#define ZYD_CR138 0x9228
+#define ZYD_CR139 0x922c
+#define ZYD_CR140 0x9230
+#define ZYD_CR141 0x9234
+#define ZYD_CR142 0x9238
+#define ZYD_CR143 0x923c
+#define ZYD_CR144 0x9240
+#define ZYD_CR145 0x9244
+#define ZYD_CR146 0x9248
+#define ZYD_CR147 0x924c
+#define ZYD_CR148 0x9250
+#define ZYD_CR149 0x9254
+#define ZYD_CR150 0x9258
+#define ZYD_CR151 0x925c
+#define ZYD_CR152 0x9260
+#define ZYD_CR153 0x9264
+#define ZYD_CR154 0x9268
+#define ZYD_CR155 0x926c
+#define ZYD_CR156 0x9270
+#define ZYD_CR157 0x9274
+#define ZYD_CR158 0x9278
+#define ZYD_CR159 0x927c
+#define ZYD_CR160 0x9280
+#define ZYD_CR161 0x9284
+#define ZYD_CR162 0x9288
+#define ZYD_CR163 0x928c
+#define ZYD_CR164 0x9290
+#define ZYD_CR165 0x9294
+#define ZYD_CR166 0x9298
+#define ZYD_CR167 0x929c
+#define ZYD_CR168 0x92a0
+#define ZYD_CR169 0x92a4
+#define ZYD_CR170 0x92a8
+#define ZYD_CR171 0x92ac
+#define ZYD_CR172 0x92b0
+#define ZYD_CR173 0x92b4
+#define ZYD_CR174 0x92b8
+#define ZYD_CR175 0x92bc
+#define ZYD_CR176 0x92c0
+#define ZYD_CR177 0x92c4
+#define ZYD_CR178 0x92c8
+#define ZYD_CR179 0x92cc
+#define ZYD_CR180 0x92d0
+#define ZYD_CR181 0x92d4
+#define ZYD_CR182 0x92d8
+#define ZYD_CR183 0x92dc
+#define ZYD_CR184 0x92e0
+#define ZYD_CR185 0x92e4
+#define ZYD_CR186 0x92e8
+#define ZYD_CR187 0x92ec
+#define ZYD_CR188 0x92f0
+#define ZYD_CR189 0x92f4
+#define ZYD_CR190 0x92f8
+#define ZYD_CR191 0x92fc
+#define ZYD_CR192 0x9300
+#define ZYD_CR193 0x9304
+#define ZYD_CR194 0x9308
+#define ZYD_CR195 0x930c
+#define ZYD_CR196 0x9310
+#define ZYD_CR197 0x9314
+#define ZYD_CR198 0x9318
+#define ZYD_CR199 0x931c
+#define ZYD_CR200 0x9320
+#define ZYD_CR201 0x9324
+#define ZYD_CR202 0x9328
+#define ZYD_CR203 0x932c
+#define ZYD_CR204 0x9330
+#define ZYD_CR205 0x9334
+#define ZYD_CR206 0x9338
+#define ZYD_CR207 0x933c
+#define ZYD_CR208 0x9340
+#define ZYD_CR209 0x9344
+#define ZYD_CR210 0x9348
+#define ZYD_CR211 0x934c
+#define ZYD_CR212 0x9350
+#define ZYD_CR213 0x9354
+#define ZYD_CR214 0x9358
+#define ZYD_CR215 0x935c
+#define ZYD_CR216 0x9360
+#define ZYD_CR217 0x9364
+#define ZYD_CR218 0x9368
+#define ZYD_CR219 0x936c
+#define ZYD_CR220 0x9370
+#define ZYD_CR221 0x9374
+#define ZYD_CR222 0x9378
+#define ZYD_CR223 0x937c
+#define ZYD_CR224 0x9380
+#define ZYD_CR225 0x9384
+#define ZYD_CR226 0x9388
+#define ZYD_CR227 0x938c
+#define ZYD_CR228 0x9390
+#define ZYD_CR229 0x9394
+#define ZYD_CR230 0x9398
+#define ZYD_CR231 0x939c
+#define ZYD_CR232 0x93a0
+#define ZYD_CR233 0x93a4
+#define ZYD_CR234 0x93a8
+#define ZYD_CR235 0x93ac
+#define ZYD_CR236 0x93b0
+#define ZYD_CR240 0x93c0
+#define ZYD_CR241 0x93c4
+#define ZYD_CR242 0x93c8
+#define ZYD_CR243 0x93cc
+#define ZYD_CR244 0x93d0
+#define ZYD_CR245 0x93d4
+#define ZYD_CR251 0x93ec
+#define ZYD_CR252 0x93f0
+#define ZYD_CR253 0x93f4
+#define ZYD_CR254 0x93f8
+#define ZYD_CR255 0x93fc
+
+/* copied nearly verbatim from the Linux driver rewrite */
+#define ZYD_DEF_PHY \
+{ \
+ { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \
+ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \
+ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \
+ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \
+ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \
+ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \
+ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \
+ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \
+ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \
+ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \
+ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \
+ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \
+ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \
+ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \
+ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \
+ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \
+ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \
+ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \
+ { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \
+ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \
+ { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \
+ { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \
+ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \
+ { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \
+ { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \
+ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \
+ { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \
+ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \
+ { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \
+ { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \
+ { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \
+ { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \
+ { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \
+ { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \
+ { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \
+ { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \
+ { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \
+ { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \
+ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \
+ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \
+ { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \
+ { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \
+ { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \
+ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \
+ { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \
+ { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \
+ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \
+ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \
+ { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \
+ { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \
+ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \
+ { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \
+ { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \
+ { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \
+ { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \
+ { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \
+ { ZYD_CR203, 0x30 }, { 0, 0} \
+}
+
+#define ZYD_DEF_PHYB \
+{ \
+ { ZYD_CR0, 0x14 }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \
+ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xe0 }, \
+ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0xf0 }, \
+ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \
+ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \
+ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x10 }, { ZYD_CR21, 0x0e }, \
+ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x10 }, \
+ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \
+ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \
+ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \
+ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \
+ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \
+ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x33 }, { ZYD_CR46, 0xff }, \
+ { ZYD_CR47, 0x1E }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \
+ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \
+ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \
+ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \
+ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \
+ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \
+ { ZYD_CR79, 0xf0 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \
+ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \
+ { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0c }, { ZYD_CR87, 0x12 }, \
+ { ZYD_CR88, 0x0c }, { ZYD_CR89, 0x00 }, { ZYD_CR90, 0x58 }, \
+ { ZYD_CR91, 0x04 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x00 }, \
+ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x20 }, { ZYD_CR96, 0x50 }, \
+ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR99, 0x00 }, \
+ { ZYD_CR100, 0x01 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \
+ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \
+ { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, \
+ { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, \
+ { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfc }, \
+ { ZYD_CR118, 0xfa }, { ZYD_CR119, 0x1e }, { ZYD_CR125, 0x90 }, \
+ { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, { ZYD_CR128, 0x14 }, \
+ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0c }, \
+ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0xa0 }, { ZYD_CR138, 0xa8 }, \
+ { ZYD_CR139, 0xb4 }, { ZYD_CR140, 0x98 }, { ZYD_CR141, 0x82 }, \
+ { ZYD_CR142, 0x53 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \
+ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x40 }, { ZYD_CR149, 0x40 }, \
+ { ZYD_CR150, 0x14 }, { ZYD_CR151, 0x18 }, { ZYD_CR159, 0x70 }, \
+ { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \
+ { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \
+ { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \
+ { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \
+ { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \
+ { 0, 0 } \
+}
+
+#define ZYD_RFMD_PHY \
+{ \
+ { ZYD_CR2, 0x1e }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \
+ { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xd0 }, { ZYD_CR17, 0x68 }, \
+ { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0e }, \
+ { ZYD_CR23, 0x48 }, { ZYD_CR24, 0x14 }, { ZYD_CR26, 0x90 }, \
+ { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, { ZYD_CR31, 0xb2 }, \
+ { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 }, \
+ { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xf0 }, { ZYD_CR41, 0x2a }, \
+ { ZYD_CR46, 0x7f }, { ZYD_CR47, 0x1e }, { ZYD_CR51, 0xc5 }, \
+ { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR82, 0x00 }, \
+ { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, \
+ { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2a }, { ZYD_CR88, 0x10 }, \
+ { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, { ZYD_CR91, 0x00 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \
+ { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \
+ { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \
+ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \
+ { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, { ZYD_CR106, 0x1a }, \
+ { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x2f }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \
+ { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xf0 }, { ZYD_CR118, 0xf0 }, \
+ { ZYD_CR119, 0x16 }, { ZYD_CR122, 0x00 }, { ZYD_CR127, 0x03 }, \
+ { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, { ZYD_CR148, 0x44 }, \
+ { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xbb }, { ZYD_CR170, 0xbb } \
+}
+
+#define ZYD_RFMD_RF \
+{ \
+ 0x000007, 0x07dd43, 0x080959, 0x0e6666, 0x116a57, 0x17dd43, \
+ 0x1819f9, 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, 0x294128, \
+ 0x2c0000, 0x300000, 0x340000, 0x381e0f, 0x6c180f \
+}
+
+#define ZYD_RFMD_CHANTABLE \
+{ \
+ { 0x181979, 0x1e6666 }, \
+ { 0x181989, 0x1e6666 }, \
+ { 0x181999, 0x1e6666 }, \
+ { 0x1819a9, 0x1e6666 }, \
+ { 0x1819b9, 0x1e6666 }, \
+ { 0x1819c9, 0x1e6666 }, \
+ { 0x1819d9, 0x1e6666 }, \
+ { 0x1819e9, 0x1e6666 }, \
+ { 0x1819f9, 0x1e6666 }, \
+ { 0x181a09, 0x1e6666 }, \
+ { 0x181a19, 0x1e6666 }, \
+ { 0x181a29, 0x1e6666 }, \
+ { 0x181a39, 0x1e6666 }, \
+ { 0x181a60, 0x1c0000 } \
+}
+
+#define ZYD_AL2230_PHY \
+{ \
+ { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \
+ { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \
+ { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \
+ { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \
+ { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \
+ { ZYD_CR17, 0x28 }, { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \
+ { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \
+ { ZYD_CR46, 0x96 }, { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, \
+ { ZYD_CR89, 0x04 }, { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, \
+ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \
+ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \
+ { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \
+ { ZYD_CR253, 0xff }, { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, \
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } \
+}
+
+#define ZYD_AL2230_PHY_B \
+{ \
+ { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \
+ { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \
+ { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \
+ { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \
+ { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \
+ { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \
+ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \
+ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \
+ { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \
+ { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \
+ { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \
+ { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \
+ { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \
+}
+
+#define ZYD_AL2230_PHY_PART1 \
+{ \
+ { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \
+}
+
+#define ZYD_AL2230_PHY_PART2 \
+{ \
+ { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \
+}
+
+#define ZYD_AL2230_PHY_PART3 \
+{ \
+ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \
+}
+
+#define ZYD_AL2230S_PHY_INIT \
+{ \
+ { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \
+ { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \
+ { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \
+ { ZYD_CR130, 0x10 } \
+}
+
+#define ZYD_AL2230_PHY_FINI_PART1 \
+{ \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \
+ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \
+}
+
+#define ZYD_AL2230_RF_PART1 \
+{ \
+ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \
+}
+
+#define ZYD_AL2230_RF_PART2 \
+{ \
+ 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \
+ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \
+}
+
+#define ZYD_AL2230_RF_PART3 \
+{ \
+ 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \
+}
+
+#define ZYD_AL2230_RF_B \
+{ \
+ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \
+ 0x0005a4, 0x0f4dc5, 0x0805b6, 0x0146c7, 0x000688, 0x0403b9, \
+ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \
+}
+
+#define ZYD_AL2230_RF_B_PART1 \
+{ \
+ 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \
+}
+
+#define ZYD_AL2230_RF_B_PART2 \
+{ \
+ 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \
+ 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \
+}
+
+#define ZYD_AL2230_RF_B_PART3 \
+{ \
+ 0xf01b00, 0xf01e00, 0xf01a00 \
+}
+
+#define ZYD_AL2230_CHANTABLE \
+{ \
+ { 0x03f790, 0x033331, 0x00000d }, \
+ { 0x03f790, 0x0b3331, 0x00000d }, \
+ { 0x03e790, 0x033331, 0x00000d }, \
+ { 0x03e790, 0x0b3331, 0x00000d }, \
+ { 0x03f7a0, 0x033331, 0x00000d }, \
+ { 0x03f7a0, 0x0b3331, 0x00000d }, \
+ { 0x03e7a0, 0x033331, 0x00000d }, \
+ { 0x03e7a0, 0x0b3331, 0x00000d }, \
+ { 0x03f7b0, 0x033331, 0x00000d }, \
+ { 0x03f7b0, 0x0b3331, 0x00000d }, \
+ { 0x03e7b0, 0x033331, 0x00000d }, \
+ { 0x03e7b0, 0x0b3331, 0x00000d }, \
+ { 0x03f7c0, 0x033331, 0x00000d }, \
+ { 0x03e7c0, 0x066661, 0x00000d } \
+}
+
+#define ZYD_AL2230_CHANTABLE_B \
+{ \
+ { 0x09efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x09efc0, 0x8cccd0, 0xb00000 }, \
+ { 0x09e7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x09e7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x05efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x05efc0, 0x8cccd0, 0xb00000 }, \
+ { 0x05e7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x05e7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x0defc0, 0x8cccc0, 0xb00000 }, \
+ { 0x0defc0, 0x8cccd0, 0xb00000 }, \
+ { 0x0de7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x0de7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x03efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x03e7c0, 0x866660, 0xb00000 } \
+}
+
+#define ZYD_AL7230B_PHY_1 \
+{ \
+ { ZYD_CR240, 0x57 }, { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, \
+ { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x22 }, \
+ { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, \
+ { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, \
+ { ZYD_CR122, 0xfc }, { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x28 }, \
+ { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, \
+ { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x96 }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \
+ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x02 }, \
+ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x22 }, \
+ { ZYD_CR107, 0x3f }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x1f }, \
+ { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \
+ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, { ZYD_CR116, 0x3f }, \
+ { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfc }, { ZYD_CR119, 0x10 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR137, 0x88 }, \
+ { ZYD_CR138, 0xa8 }, { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 }, \
+ { ZYD_CR251, 0x2f } \
+}
+
+#define ZYD_AL7230B_PHY_2 \
+{ \
+ { ZYD_CR251, 0x3f }, { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, \
+ { ZYD_CR130, 0x10 }, { ZYD_CR38, 0x38 }, { ZYD_CR136, 0xdf } \
+}
+
+#define ZYD_AL7230B_PHY_3 \
+{ \
+ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 } \
+}
+
+#define ZYD_AL7230B_RF_1 \
+{ \
+ 0x09ec04, 0x8cccc8, 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, \
+ 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, \
+ 0x3c9000, 0xbfffff, 0x700000, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_RF_2 \
+{ \
+ 0xf15d59, 0xf15d5c, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_RF_SETCHANNEL \
+{ \
+ 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, 0x6cf56a, 0xe04073, \
+ 0x193d76, 0x9dd844, 0x500007, 0xd8c010, 0x3c9000, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_CHANTABLE \
+{ \
+ { 0x09ec00, 0x8cccc8 }, \
+ { 0x09ec00, 0x8cccd8 }, \
+ { 0x09ec00, 0x8cccc0 }, \
+ { 0x09ec00, 0x8cccd0 }, \
+ { 0x05ec00, 0x8cccc8 }, \
+ { 0x05ec00, 0x8cccd8 }, \
+ { 0x05ec00, 0x8cccc0 }, \
+ { 0x05ec00, 0x8cccd0 }, \
+ { 0x0dec00, 0x8cccc8 }, \
+ { 0x0dec00, 0x8cccd8 }, \
+ { 0x0dec00, 0x8cccc0 }, \
+ { 0x0dec00, 0x8cccd0 }, \
+ { 0x03ec00, 0x8cccc8 }, \
+ { 0x03ec00, 0x866660 } \
+}
+
+#define ZYD_AL2210_PHY \
+{ \
+ { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \
+ { ZYD_CR15, 0xd0 }, { ZYD_CR16, 0x40 }, { ZYD_CR17, 0x58 }, \
+ { ZYD_CR18, 0x04 }, { ZYD_CR23, 0x66 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR26, 0x90 }, { ZYD_CR31, 0x80 }, { ZYD_CR34, 0x06 }, \
+ { ZYD_CR35, 0x3e }, { ZYD_CR38, 0x38 }, { ZYD_CR46, 0x90 }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR64, 0x64 }, { ZYD_CR79, 0xb5 }, \
+ { ZYD_CR80, 0x38 }, { ZYD_CR81, 0x30 }, { ZYD_CR113, 0xc0 }, \
+ { ZYD_CR127, 0x03 } \
+}
+
+#define ZYD_AL2210_RF \
+{ \
+ 0x2396c0, 0x00fcb1, 0x358132, 0x0108b3, 0xc77804, 0x456415, \
+ 0xff2226, 0x806667, 0x7860f8, 0xbb01c9, 0x00000a, 0x00000b \
+}
+
+#define ZYD_AL2210_CHANTABLE \
+{ \
+ 0x0196c0, 0x019710, 0x019760, 0x0197b0, 0x019800, 0x019850, \
+ 0x0198a0, 0x0198f0, 0x019940, 0x019990, 0x0199e0, 0x019a30, \
+ 0x019a80, 0x019b40 \
+}
+
+#define ZYD_GCT_PHY \
+{ \
+ { ZYD_CR47, 0x1e }, { ZYD_CR15, 0xdc }, { ZYD_CR113, 0xc0 }, \
+ { ZYD_CR20, 0x0c }, { ZYD_CR17, 0x65 }, { ZYD_CR34, 0x04 }, \
+ { ZYD_CR35, 0x35 }, { ZYD_CR24, 0x20 }, { ZYD_CR9, 0xe0 }, \
+ { ZYD_CR127, 0x02 }, { ZYD_CR10, 0x91 }, { ZYD_CR23, 0x7f }, \
+ { ZYD_CR27, 0x10 }, { ZYD_CR28, 0x7a }, { ZYD_CR79, 0xb5 }, \
+ { ZYD_CR64, 0x80 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 } \
+}
+
+#define ZYD_GCT_RF \
+{ \
+ 0x1f0000, 0x1f0000, 0x1f0200, 0x1f0600, 0x1f8600, 0x1f8600, \
+ 0x002050, 0x1f8000, 0x1f8200, 0x1f8600, 0x1c0000, 0x10c458, \
+ 0x088e92, 0x187b82, 0x0401b4, 0x140816, 0x0c7000, 0x1c0000, \
+ 0x02ccae, 0x128023, 0x0a0000, 0x1a0000, 0x06e380, 0x16cb94, \
+ 0x0e1740, 0x014980, 0x116240, 0x090000, 0x192304, 0x05112f, \
+ 0x0d54a8, 0x0f8000, 0x1c0008, 0x1c0000, 0x1a0000, 0x1c0008, \
+ 0x150000, 0x0c7000, 0x150800, 0x150000 \
+}
+
+#define ZYD_GCT_CHANTABLE \
+{ \
+ 0x1a0000, 0x1a8000, 0x1a4000, 0x1ac000, 0x1a2000, 0x1aa000, \
+ 0x1a6000, 0x1ae000, 0x1a1000, 0x1a9000, 0x1a5000, 0x1ad000, \
+ 0x1a3000, 0x1ab000 \
+}
+
+#define ZYD_MAXIM_PHY \
+{ \
+ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \
+ { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \
+ { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe }, \
+ { ZYD_CR150, 0x0d } \
+}
+
+#define ZYD_MAXIM_RF \
+{ \
+ 0x00ccd4, 0x030a03, 0x000400, 0x000ca1, 0x010072, 0x018645, \
+ 0x004006, 0x0000a7, 0x008258, 0x003fc9, 0x00040a, 0x00000b, \
+ 0x00026c \
+}
+
+#define ZYD_MAXIM_CHANTABLE \
+{ \
+ { 0x0ccd4, 0x30a03 }, \
+ { 0x22224, 0x00a13 }, \
+ { 0x37774, 0x10a13 }, \
+ { 0x0ccd4, 0x30a13 }, \
+ { 0x22224, 0x00a23 }, \
+ { 0x37774, 0x10a23 }, \
+ { 0x0ccd4, 0x30a23 }, \
+ { 0x22224, 0x00a33 }, \
+ { 0x37774, 0x10a33 }, \
+ { 0x0ccd4, 0x30a33 }, \
+ { 0x22224, 0x00a43 }, \
+ { 0x37774, 0x10a43 }, \
+ { 0x0ccd4, 0x30a43 }, \
+ { 0x199a4, 0x20a53 } \
+}
+
+#define ZYD_MAXIM2_PHY \
+{ \
+ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \
+ { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \
+ { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe } \
+}
+
+#define ZYD_MAXIM2_RF \
+{ \
+ 0x33334, 0x10a03, 0x00400, 0x00ca1, 0x10072, 0x18645, 0x04006, \
+ 0x000a7, 0x08258, 0x03fc9, 0x0040a, 0x0000b, 0x0026c \
+}
+
+#define ZYD_MAXIM2_CHANTABLE_F \
+{ \
+ 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, \
+ 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x26664 \
+}
+
+#define ZYD_MAXIM2_CHANTABLE \
+{ \
+ { 0x33334, 0x10a03 }, \
+ { 0x08884, 0x20a13 }, \
+ { 0x1ddd4, 0x30a13 }, \
+ { 0x33334, 0x10a13 }, \
+ { 0x08884, 0x20a23 }, \
+ { 0x1ddd4, 0x30a23 }, \
+ { 0x33334, 0x10a23 }, \
+ { 0x08884, 0x20a33 }, \
+ { 0x1ddd4, 0x30a33 }, \
+ { 0x33334, 0x10a33 }, \
+ { 0x08884, 0x20a43 }, \
+ { 0x1ddd4, 0x30a43 }, \
+ { 0x33334, 0x10a43 }, \
+ { 0x26664, 0x20a53 } \
+}
+
+/*
+ * Control pipe requests.
+ */
+#define ZYD_DOWNLOADREQ 0x30
+#define ZYD_DOWNLOADSTS 0x31
+#define ZYD_READFWDATAREQ 0x32
+
+/* possible values for register ZYD_CR_INTERRUPT */
+#define ZYD_HWINT_MASK 0x004f0000
+
+/* possible values for register ZYD_MAC_MISC */
+#define ZYD_UNLOCK_PHY_REGS 0x80
+
+/* possible values for register ZYD_MAC_ENCRYPTION_TYPE */
+#define ZYD_ENC_SNIFFER 8
+
+/* flags for register ZYD_MAC_RXFILTER */
+#define ZYD_FILTER_ASS_REQ (1 << 0)
+#define ZYD_FILTER_ASS_RSP (1 << 1)
+#define ZYD_FILTER_REASS_REQ (1 << 2)
+#define ZYD_FILTER_REASS_RSP (1 << 3)
+#define ZYD_FILTER_PRB_REQ (1 << 4)
+#define ZYD_FILTER_PRB_RSP (1 << 5)
+#define ZYD_FILTER_BCN (1 << 8)
+#define ZYD_FILTER_ATIM (1 << 9)
+#define ZYD_FILTER_DEASS (1 << 10)
+#define ZYD_FILTER_AUTH (1 << 11)
+#define ZYD_FILTER_DEAUTH (1 << 12)
+#define ZYD_FILTER_PS_POLL (1 << 26)
+#define ZYD_FILTER_RTS (1 << 27)
+#define ZYD_FILTER_CTS (1 << 28)
+#define ZYD_FILTER_ACK (1 << 29)
+#define ZYD_FILTER_CFE (1 << 30)
+#define ZYD_FILTER_CFE_A (1 << 31)
+
+/* helpers for register ZYD_MAC_RXFILTER */
+#define ZYD_FILTER_MONITOR 0xffffffff
+#define ZYD_FILTER_BSS \
+ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \
+ ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \
+ ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \
+ (0x3 << 6) | \
+ ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \
+ ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \
+ (0x7 << 13) | \
+ ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK)
+#define ZYD_FILTER_HOSTAP \
+ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \
+ ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \
+ ZYD_FILTER_DEAUTH | ZYD_FILTER_PS_POLL)
+
+struct zyd_tx_desc {
+ uint8_t phy;
+#define ZYD_TX_PHY_SIGNAL(x) ((x) & 0xf)
+#define ZYD_TX_PHY_OFDM (1 << 4)
+#define ZYD_TX_PHY_SHPREAMBLE (1 << 5) /* CCK */
+#define ZYD_TX_PHY_5GHZ (1 << 5) /* OFDM */
+ uint16_t len;
+ uint8_t flags;
+#define ZYD_TX_FLAG_BACKOFF (1 << 0)
+#define ZYD_TX_FLAG_MULTICAST (1 << 1)
+#define ZYD_TX_FLAG_TYPE(x) (((x) & 0x3) << 2)
+#define ZYD_TX_TYPE_DATA 0
+#define ZYD_TX_TYPE_PS_POLL 1
+#define ZYD_TX_TYPE_MGMT 2
+#define ZYD_TX_TYPE_CTL 3
+#define ZYD_TX_FLAG_WAKEUP (1 << 4)
+#define ZYD_TX_FLAG_RTS (1 << 5)
+#define ZYD_TX_FLAG_ENCRYPT (1 << 6)
+#define ZYD_TX_FLAG_CTS_TO_SELF (1 << 7)
+ uint16_t pktlen;
+ uint16_t plcp_length;
+ uint8_t plcp_service;
+#define ZYD_PLCP_LENGEXT 0x80
+ uint16_t nextlen;
+} __packed;
+
+struct zyd_plcphdr {
+ uint8_t signal;
+ uint8_t reserved[2];
+ uint16_t service; /* unaligned! */
+} __packed;
+
+struct zyd_rx_stat {
+ uint8_t signal_cck;
+ uint8_t rssi;
+ uint8_t signal_ofdm;
+ uint8_t cipher;
+#define ZYD_RX_CIPHER_WEP64 1
+#define ZYD_RX_CIPHER_TKIP 2
+#define ZYD_RX_CIPHER_AES 4
+#define ZYD_RX_CIPHER_WEP128 5
+#define ZYD_RX_CIPHER_WEP256 6
+#define ZYD_RX_CIPHER_WEP \
+ (ZYD_RX_CIPHER_WEP64 | ZYD_RX_CIPHER_WEP128 | ZYD_RX_CIPHER_WEP256)
+ uint8_t flags;
+#define ZYD_RX_OFDM (1 << 0)
+#define ZYD_RX_TIMEOUT (1 << 1)
+#define ZYD_RX_OVERRUN (1 << 2)
+#define ZYD_RX_DECRYPTERR (1 << 3)
+#define ZYD_RX_BADCRC32 (1 << 4)
+#define ZYD_RX_NOT2ME (1 << 5)
+#define ZYD_RX_BADCRC16 (1 << 6)
+#define ZYD_RX_ERROR (1 << 7)
+} __packed;
+
+/* this structure may be unaligned */
+struct zyd_rx_desc {
+#define ZYD_MAX_RXFRAMECNT 3
+ uWord len[ZYD_MAX_RXFRAMECNT];
+ uWord tag;
+#define ZYD_TAG_MULTIFRAME 0x697e
+} __packed;
+
+/* I2C bus alike */
+struct zyd_rfwrite_cmd {
+ uint16_t code;
+ uint16_t width;
+ uint16_t bit[32];
+#define ZYD_RF_IF_LE (1 << 1)
+#define ZYD_RF_CLK (1 << 2)
+#define ZYD_RF_DATA (1 << 3)
+} __packed;
+
+struct zyd_cmd {
+ uint16_t code;
+#define ZYD_CMD_IOWR 0x0021 /* write HMAC or PHY register */
+#define ZYD_CMD_IORD 0x0022 /* read HMAC or PHY register */
+#define ZYD_CMD_RFCFG 0x0023 /* write RF register */
+#define ZYD_NOTIF_IORD 0x9001 /* response for ZYD_CMD_IORD */
+#define ZYD_NOTIF_MACINTR 0x9001 /* interrupt notification */
+#define ZYD_NOTIF_RETRYSTATUS 0xa001 /* Tx retry notification */
+ uint8_t data[64];
+} __packed;
+
+/* structure for command ZYD_CMD_IOWR */
+struct zyd_pair {
+ uint16_t reg;
+/* helpers macros to read/write 32-bit registers */
+#define ZYD_REG32_LO(reg) (reg)
+#define ZYD_REG32_HI(reg) \
+ ((reg) + ((((reg) & 0xf000) == 0x9000) ? 2 : 1))
+ uint16_t val;
+} __packed;
+
+/* structure for notification ZYD_NOTIF_RETRYSTATUS */
+struct zyd_notif_retry {
+ uint16_t rate;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint16_t count;
+} __packed;
+
+#define ZYD_CONFIG_NO 1
+#define ZYD_IFACE_INDEX 0
+
+#define ZYD_INTR_TIMEOUT 1000
+#define ZYD_TX_TIMEOUT 10000
+
+#define ZYD_MAX_TXBUFSZ \
+ (sizeof(struct zyd_tx_desc) + MCLBYTES)
+#define ZYD_MIN_FRAGSZ \
+ (sizeof(struct zyd_plcphdr) + IEEE80211_MIN_LEN + \
+ sizeof(struct zyd_rx_stat))
+#define ZYD_MIN_RXBUFSZ ZYD_MIN_FRAGSZ
+#define ZYX_MAX_RXBUFSZ \
+ ((sizeof (struct zyd_plcphdr) + IEEE80211_MAX_LEN + \
+ sizeof (struct zyd_rx_stat)) * ZYD_MAX_RXFRAMECNT + \
+ sizeof (struct zyd_rx_desc))
+
+#define ZYD_RX_LIST_CNT 1
+#define ZYD_TX_LIST_CNT 5
+#define ZYD_CMD_FLAG_READ (1 << 0)
+
+/* quickly determine if a given rate is CCK or OFDM */
+#define ZYD_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
+
+struct zyd_phy_pair {
+ uint16_t reg;
+ uint8_t val;
+};
+
+struct zyd_mac_pair {
+ uint16_t reg;
+ uint32_t val;
+};
+
+struct zyd_tx_data {
+ struct zyd_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+};
+
+struct zyd_rx_data {
+ struct zyd_softc *sc;
+ usbd_xfer_handle xfer;
+ const uint8_t *buf;
+};
+
+struct zyd_node {
+ struct ieee80211_node ni; /* must be the first */
+ struct ieee80211_amrr_node amn;
+};
+#define ZYD_NODE(ni) ((struct zyd_node *)(ni))
+
+struct zyd_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+ int8_t wr_antnoise;
+} __packed;
+
+#define ZYD_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct zyd_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed;
+
+#define ZYD_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct zyd_softc; /* forward declaration */
+
+struct zyd_rf {
+ /* RF methods */
+ int (*init)(struct zyd_rf *);
+ int (*switch_radio)(struct zyd_rf *, int);
+ int (*set_channel)(struct zyd_rf *, uint8_t);
+ int (*bandedge6)(struct zyd_rf *,
+ struct ieee80211_channel *);
+ /* RF attributes */
+ struct zyd_softc *rf_sc; /* back-pointer */
+ int width;
+};
+
+struct zyd_rq {
+ const uint16_t *idata;
+ struct zyd_pair *odata;
+ int len;
+ STAILQ_ENTRY(zyd_rq) rq;
+};
+
+struct zyd_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ struct callout amrr_ch;
+ struct ieee80211_amrr amrr;
+};
+#define ZYD_VAP(vap) ((struct zyd_vap *)(vap))
+
+struct zyd_softc {
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+ struct ifnet *sc_ifp;
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int sc_flags;
+#define ZYD_FLAG_FWLOADED (1 << 0)
+#define ZYD_FLAG_DETACHING (1 << 1)
+#define ZYD_FLAG_INITONCE (1 << 2)
+#define ZYD_FLAG_INITDONE (1 << 3)
+ int sc_if_flags;
+ uint32_t sc_debug;
+
+ struct usb_task sc_mcasttask;
+ struct usb_task sc_scantask;
+ int sc_scan_action;
+#define ZYD_SCAN_START 0
+#define ZYD_SCAN_END 1
+#define ZYD_SET_CHANNEL 2
+ struct usb_task sc_task;
+ struct callout sc_watchdog_ch;
+
+ struct zyd_rf sc_rf;
+
+ STAILQ_HEAD(, zyd_rq) sc_rqh;
+
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+ uint16_t sc_fwbase;
+ uint8_t sc_regdomain;
+ uint8_t sc_macrev;
+ uint16_t sc_fwrev;
+ uint8_t sc_rfrev;
+ uint8_t sc_parev;
+ uint8_t sc_al2230s;
+ uint8_t sc_bandedge6;
+ uint8_t sc_newphy;
+ uint8_t sc_cckgain;
+ uint8_t sc_fix_cr157;
+ uint8_t sc_ledtype;
+ uint8_t sc_txled;
+
+ uint32_t sc_atim_wnd;
+ uint32_t sc_pre_tbtt;
+ uint32_t sc_bcn_int;
+
+ uint8_t sc_pwrcal[14];
+ uint8_t sc_pwrint[14];
+ uint8_t sc_ofdm36_cal[14];
+ uint8_t sc_ofdm48_cal[14];
+ uint8_t sc_ofdm54_cal[14];
+#define ZYD_ENDPT_BOUT 0
+#define ZYD_ENDPT_BIN 1
+#define ZYD_ENDPT_IIN 2
+#define ZYD_ENDPT_IOUT 3
+#define ZYD_ENDPT_CNT 4
+ usbd_pipe_handle sc_ep[ZYD_ENDPT_CNT];
+ uint8_t *sc_ibuf;
+
+ struct mtx sc_txmtx;
+ struct zyd_rx_data sc_rxdata[ZYD_RX_LIST_CNT];
+ struct zyd_tx_data sc_txdata[ZYD_TX_LIST_CNT];
+ int sc_txidx;
+ int sc_txqueued;
+ int sc_txtimer;
+
+ struct zyd_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+ struct zyd_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#define ZYD_LOCK(sc) do { ((sc) = (sc)); mtx_lock(&Giant); } while (0)
+#define ZYD_UNLOCK(sc) mtx_unlock(&Giant)
+#define ZYD_TX_LOCK(sc) mtx_lock(&(sc)->sc_txmtx)
+#define ZYD_TX_UNLOCK(sc) mtx_unlock(&(sc)->sc_txmtx)
+
diff --git a/sys/legacy/dev/usb/kue_fw.h b/sys/legacy/dev/usb/kue_fw.h
new file mode 100644
index 0000000..8934465
--- /dev/null
+++ b/sys/legacy/dev/usb/kue_fw.h
@@ -0,0 +1,685 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This file contains the firmware needed to make the KLSI chip work,
+ * along with a few constants related to the QT Engine microcontroller
+ * embedded in the KLSI part.
+ *
+ * Firmware is loaded using the vendor-specific 'send scan data'
+ * command (0xFF). The basic operation is that we must load the
+ * firmware, then issue some trigger commands to fix it up and start
+ * it running. There are three transfers: load the binary code,
+ * load the 'fixup' (data segment?), then issue a command to
+ * start the code firmware running. The data itself is prefixed by
+ * a 16-bit signature word, a 16-bit length value, a type byte
+ * and an interrupt (command) byte. The code segment is of type
+ * 0x02 (replacement interrupt vector data) and the fixup segment
+ * is of type 0x03 (replacement interrupt fixup data). The interrupt
+ * code is 0x64 (load new code). The length word is the total length
+ * of the segment minus 7. I precomputed the values and stuck them
+ * into the appropriate locations within the segments to save some
+ * work in the driver.
+ */
+
+/* QT controller data block types. */
+/* Write data into specific memory location. */
+#define KUE_QTBTYPE_WRITE_DATA 0x00
+/* Write data into interrupt vector location */
+#define KUE_QTBTYPE_WRITE_INTVEC 0x01
+/* Replace interrupt vector with this data */
+#define KUE_QTBTYPE_REPL_INTVEC 0x02
+/* Fixup interrupt vector code with this data */
+#define KUE_QTBTYPE_FIXUP_INTVEC 0x03
+/* Force jump to location */
+#define KUE_QTBTYPE_JUMP 0x04
+/* Force call to location */
+#define KUE_QTBTYPE_CALL 0x05
+/* Force interrupt call */
+#define KUE_QTBTYPE_CALLINTR 0x06
+/*
+ * Cause data to be written using the specified QT engine
+ * interrupt, from starting location in memory for a specified
+ * number of bytes.
+ */
+#define KUE_QTBTYPE_WRITE_WITH_INTR 0x07
+/* Cause data from stream to be written using specified QT interrupt. */
+#define KUE_QTBTYPE_WRITE_STR_WITH_INTR 0x08
+/* Cause data to be written to config locations. */
+/* Addresses assume 0xc000 offset. */
+#define KUE_QTBTYPE_WRITE_CONFIG 0x09
+
+#define KUE_QTINTR_LOAD_CODE 0x64
+#define KUE_QTINTR_TRIGGER_CODE 0x3B
+#define KUE_QTINTR_LOAD_CODE_HIGH 0x9C
+
+/* Firmware code segment */
+static unsigned char kue_code_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xf7, 0x0e, 0x02, 0x64,
+ 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00,
+ 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f,
+ 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09,
+ 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08,
+ 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08,
+ 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1,
+ 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00,
+ 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf,
+ 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09,
+ 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0,
+ 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08,
+ 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08,
+ 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57,
+ 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00,
+ 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09,
+ 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59,
+ 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00,
+ 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1,
+ 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03,
+ 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1,
+ 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08,
+ 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04,
+ 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09,
+ 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00,
+ 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08,
+ 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08,
+ 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00,
+ 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00,
+ 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf,
+ 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17,
+ 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08,
+ 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0,
+ 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37,
+ 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60,
+ 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9,
+ 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17,
+ 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00,
+ 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04,
+ 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf,
+ 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf,
+ 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf,
+ 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57,
+ 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02,
+ 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01,
+ 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02,
+ 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1,
+ 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02,
+ 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07,
+ 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00,
+ 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77,
+ 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda,
+ 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0,
+ 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf,
+ 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8,
+ 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77,
+ 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50,
+ 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00,
+ 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02,
+ 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0,
+ 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00,
+ 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07,
+ 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08,
+ 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07,
+ 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08,
+ 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09,
+ 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00,
+ 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf,
+ 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08,
+ 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05,
+ 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67,
+ 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0,
+ 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00,
+ 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08,
+ 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80,
+ 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08,
+ 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0,
+ 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00,
+ 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00,
+ 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17,
+ 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07,
+ 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03,
+ 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0,
+ 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07,
+ 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00,
+ 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08,
+ 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08,
+ 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08,
+ 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00,
+ 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09,
+ 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52,
+ 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08,
+ 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17,
+ 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05,
+ 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04,
+ 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62,
+ 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77,
+ 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9,
+ 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1,
+ 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00,
+ 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff,
+ 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17,
+ 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07,
+ 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1,
+ 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59,
+ 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf,
+ 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07,
+ 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77,
+ 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00,
+ 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00,
+ 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00,
+ 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0,
+ 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77,
+ 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf,
+ 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1,
+ 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1,
+ 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf,
+ 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
+ 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09,
+ 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53,
+ 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8,
+ 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30,
+ 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf,
+ 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30,
+ 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09,
+ 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09,
+ 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0,
+ 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01,
+ 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07,
+ 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37,
+ 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08,
+ 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08,
+ 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08,
+ 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00,
+ 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02,
+ 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00,
+ 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00,
+ 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04,
+ 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09,
+ 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00,
+ 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05,
+ 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf,
+ 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0,
+ 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09,
+ 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00,
+ 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf,
+ 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57,
+ 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77,
+ 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00,
+ 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17,
+ 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1,
+ 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02,
+ 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77,
+ 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00,
+ 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf,
+ 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57,
+ 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8,
+ 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08,
+ 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09,
+ 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77,
+ 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57,
+ 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19,
+ 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17,
+ 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf,
+ 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b,
+ 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05,
+ 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04,
+ 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf,
+ 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80,
+ 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e,
+ 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00,
+ 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09,
+ 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00,
+ 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07,
+ 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13,
+ 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08,
+ 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09,
+ 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09,
+ 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b,
+ 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00,
+ 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90,
+ 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00,
+ 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b,
+ 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00,
+ 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07,
+ 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00,
+ 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00,
+ 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf,
+ 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf,
+ 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08,
+ 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf,
+ 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94,
+ 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09,
+ 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf,
+ 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00,
+ 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00,
+ 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00,
+ 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00,
+ 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0,
+ 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0,
+ 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a,
+ 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a,
+ 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b,
+ 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02,
+ 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00,
+ 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57,
+ 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37,
+ 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2,
+ 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00,
+ 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57,
+ 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b,
+ 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07,
+ 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08,
+ 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08,
+ 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0,
+ 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00,
+ 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf,
+ 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09,
+ 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08,
+ 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c,
+ 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf,
+ 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2,
+ 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08,
+ 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07,
+ 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb,
+ 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf,
+ 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57,
+ 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07,
+ 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08,
+ 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07,
+ 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf,
+ 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00,
+ 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf,
+ 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1,
+ 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf,
+ 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf,
+ 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07,
+ 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02,
+ 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80,
+ 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf,
+ 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08,
+ 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf,
+ 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07,
+ 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1,
+ 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00,
+ 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde,
+ 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2,
+ 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08,
+ 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf,
+ 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08,
+ 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde,
+ 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67,
+ 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00,
+ 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf,
+ 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0,
+ 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00,
+ 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08,
+ 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00,
+ 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00,
+ 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09,
+ 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07,
+ 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1,
+ 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf,
+ 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07,
+ 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00,
+ 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00,
+ 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09,
+ 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02,
+ 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda,
+ 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08,
+ 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08,
+ 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02,
+ 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1,
+ 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08,
+ 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08,
+ 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08,
+ 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07,
+ 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00,
+ 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09,
+ 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00,
+ 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0,
+ 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf,
+ 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf,
+ 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00,
+ 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d,
+ 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1,
+ 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87,
+ 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08,
+ 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0,
+ 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87,
+ 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7,
+ 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0,
+ 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0,
+ 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09,
+ 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2,
+ 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02,
+ 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08,
+ 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77,
+ 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf,
+ 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e,
+ 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0,
+ 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77,
+ 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57,
+ 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb,
+ 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00,
+ 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09,
+ 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08,
+ 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09,
+ 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02,
+ 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05,
+ 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1,
+ 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02,
+ 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00,
+ 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04,
+ 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf,
+ 0, 0
+};
+
+/* Firmware fixup (data?) segment */
+static unsigned char kue_fix_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xc9, 0x02, 0x03, 0x64,
+ 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00,
+ 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00,
+ 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00,
+ 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00,
+ 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00,
+ 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00,
+ 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00,
+ 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00,
+ 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00,
+ 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00,
+ 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01,
+ 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01,
+ 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01,
+ 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01,
+ 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01,
+ 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01,
+ 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02,
+ 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02,
+ 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02,
+ 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02,
+ 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03,
+ 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03,
+ 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03,
+ 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03,
+ 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03,
+ 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03,
+ 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03,
+ 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04,
+ 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04,
+ 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04,
+ 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04,
+ 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04,
+ 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05,
+ 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05,
+ 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05,
+ 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05,
+ 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06,
+ 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06,
+ 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06,
+ 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06,
+ 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06,
+ 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06,
+ 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06,
+ 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07,
+ 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07,
+ 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08,
+ 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08,
+ 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08,
+ 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08,
+ 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09,
+ 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09,
+ 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09,
+ 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09,
+ 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09,
+ 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09,
+ 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09,
+ 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09,
+ 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a,
+ 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a,
+ 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a,
+ 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a,
+ 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a,
+ 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a,
+ 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a,
+ 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a,
+ 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a,
+ 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a,
+ 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b,
+ 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b,
+ 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b,
+ 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b,
+ 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b,
+ 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b,
+ 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c,
+ 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c,
+ 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c,
+ 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c,
+ 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c,
+ 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c,
+ 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c,
+ 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c,
+ 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d,
+ 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d,
+ 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d,
+ 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d,
+ 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d,
+ 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e,
+ 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e,
+ 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e,
+ 0, 0
+};
+
+/* Fixup command. */
+#define KUE_TRIGCMD_OFFSET 5
+static unsigned char kue_trig_seg[] = {
+0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00
+};
diff --git a/sys/legacy/dev/usb/ohci.c b/sys/legacy/dev/usb/ohci.c
new file mode 100644
index 0000000..efa6e7e
--- /dev/null
+++ b/sys/legacy/dev/usb/ohci.c
@@ -0,0 +1,3638 @@
+/* $NetBSD: ohci.c,v 1.138 2003/02/08 03:32:50 ichiro Exp $ */
+
+/* Also, already ported:
+ * $NetBSD: ohci.c,v 1.140 2003/05/13 04:42:00 gson Exp $
+ * $NetBSD: ohci.c,v 1.141 2003/09/10 20:08:29 mycroft Exp $
+ * $NetBSD: ohci.c,v 1.142 2003/10/11 03:04:26 toshii Exp $
+ * $NetBSD: ohci.c,v 1.143 2003/10/18 04:50:35 simonb Exp $
+ * $NetBSD: ohci.c,v 1.144 2003/11/23 19:18:06 augustss Exp $
+ * $NetBSD: ohci.c,v 1.145 2003/11/23 19:20:25 augustss Exp $
+ * $NetBSD: ohci.c,v 1.146 2003/12/29 08:17:10 toshii Exp $
+ * $NetBSD: ohci.c,v 1.147 2004/06/22 07:20:35 mycroft Exp $
+ * $NetBSD: ohci.c,v 1.148 2004/06/22 18:27:46 mycroft Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * 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.
+ */
+
+/*
+ * USB Open Host Controller driver.
+ *
+ * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__)
+#include <machine/cpu.h>
+#endif
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ohcireg.h>
+#include <dev/usb/ohcivar.h>
+
+#define delay(d) DELAY(d)
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ohcidebug) printf x
+#define DPRINTFN(n,x) if (ohcidebug>(n)) printf x
+int ohcidebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci");
+SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RW,
+ &ohcidebug, 0, "ohci debug level");
+#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f))
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct ohci_pipe;
+
+static ohci_soft_ed_t *ohci_alloc_sed(ohci_softc_t *);
+static void ohci_free_sed(ohci_softc_t *, ohci_soft_ed_t *);
+
+static ohci_soft_td_t *ohci_alloc_std(ohci_softc_t *);
+static void ohci_free_std(ohci_softc_t *, ohci_soft_td_t *);
+
+static ohci_soft_itd_t *ohci_alloc_sitd(ohci_softc_t *);
+static void ohci_free_sitd(ohci_softc_t *,ohci_soft_itd_t *);
+
+#if 0
+static void ohci_free_std_chain(ohci_softc_t *, ohci_soft_td_t *,
+ ohci_soft_td_t *);
+#endif
+static usbd_status ohci_alloc_std_chain(struct ohci_pipe *,
+ ohci_softc_t *, int, int, usbd_xfer_handle,
+ ohci_soft_td_t *, ohci_soft_td_t **);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static void ohci_shutdown(void *v);
+static void ohci_power(int, void *);
+#endif
+static usbd_status ohci_open(usbd_pipe_handle);
+static void ohci_poll(struct usbd_bus *);
+static void ohci_softintr(void *);
+static void ohci_waitintr(ohci_softc_t *, usbd_xfer_handle);
+static void ohci_add_done(ohci_softc_t *, ohci_physaddr_t);
+static void ohci_rhsc(ohci_softc_t *, usbd_xfer_handle);
+
+static usbd_status ohci_device_request(usbd_xfer_handle xfer);
+static void ohci_add_ed(ohci_soft_ed_t *, ohci_soft_ed_t *);
+static void ohci_rem_ed(ohci_soft_ed_t *, ohci_soft_ed_t *);
+static void ohci_hash_add_td(ohci_softc_t *, ohci_soft_td_t *);
+static void ohci_hash_rem_td(ohci_softc_t *, ohci_soft_td_t *);
+static ohci_soft_td_t *ohci_hash_find_td(ohci_softc_t *, ohci_physaddr_t);
+static void ohci_hash_add_itd(ohci_softc_t *, ohci_soft_itd_t *);
+static void ohci_hash_rem_itd(ohci_softc_t *, ohci_soft_itd_t *);
+static ohci_soft_itd_t *ohci_hash_find_itd(ohci_softc_t *, ohci_physaddr_t);
+
+static usbd_status ohci_setup_isoc(usbd_pipe_handle pipe);
+static void ohci_device_isoc_enter(usbd_xfer_handle);
+
+static usbd_status ohci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
+static void ohci_freem(struct usbd_bus *, usb_dma_t *);
+
+static usbd_xfer_handle ohci_allocx(struct usbd_bus *);
+static void ohci_freex(struct usbd_bus *, usbd_xfer_handle);
+
+static usbd_status ohci_root_ctrl_transfer(usbd_xfer_handle);
+static usbd_status ohci_root_ctrl_start(usbd_xfer_handle);
+static void ohci_root_ctrl_abort(usbd_xfer_handle);
+static void ohci_root_ctrl_close(usbd_pipe_handle);
+static void ohci_root_ctrl_done(usbd_xfer_handle);
+
+static usbd_status ohci_root_intr_transfer(usbd_xfer_handle);
+static usbd_status ohci_root_intr_start(usbd_xfer_handle);
+static void ohci_root_intr_abort(usbd_xfer_handle);
+static void ohci_root_intr_close(usbd_pipe_handle);
+static void ohci_root_intr_done(usbd_xfer_handle);
+
+static usbd_status ohci_device_ctrl_transfer(usbd_xfer_handle);
+static usbd_status ohci_device_ctrl_start(usbd_xfer_handle);
+static void ohci_device_ctrl_abort(usbd_xfer_handle);
+static void ohci_device_ctrl_close(usbd_pipe_handle);
+static void ohci_device_ctrl_done(usbd_xfer_handle);
+
+static usbd_status ohci_device_bulk_transfer(usbd_xfer_handle);
+static usbd_status ohci_device_bulk_start(usbd_xfer_handle);
+static void ohci_device_bulk_abort(usbd_xfer_handle);
+static void ohci_device_bulk_close(usbd_pipe_handle);
+static void ohci_device_bulk_done(usbd_xfer_handle);
+
+static usbd_status ohci_device_intr_transfer(usbd_xfer_handle);
+static usbd_status ohci_device_intr_start(usbd_xfer_handle);
+static void ohci_device_intr_abort(usbd_xfer_handle);
+static void ohci_device_intr_close(usbd_pipe_handle);
+static void ohci_device_intr_done(usbd_xfer_handle);
+
+static usbd_status ohci_device_isoc_transfer(usbd_xfer_handle);
+static usbd_status ohci_device_isoc_start(usbd_xfer_handle);
+static void ohci_device_isoc_abort(usbd_xfer_handle);
+static void ohci_device_isoc_close(usbd_pipe_handle);
+static void ohci_device_isoc_done(usbd_xfer_handle);
+
+static usbd_status ohci_device_setintr(ohci_softc_t *sc,
+ struct ohci_pipe *pipe, int ival);
+static usbd_status ohci_device_intr_insert(ohci_softc_t *sc,
+ usbd_xfer_handle xfer);
+
+static int ohci_str(usb_string_descriptor_t *, int, const char *);
+
+static void ohci_timeout(void *);
+static void ohci_timeout_task(void *);
+static void ohci_rhsc_able(ohci_softc_t *, int);
+static void ohci_rhsc_enable(void *);
+
+static void ohci_close_pipe(usbd_pipe_handle, ohci_soft_ed_t *);
+static void ohci_abort_xfer(usbd_xfer_handle, usbd_status);
+
+static void ohci_device_clear_toggle(usbd_pipe_handle pipe);
+static void ohci_noop(usbd_pipe_handle pipe);
+
+static usbd_status ohci_controller_init(ohci_softc_t *sc);
+
+#ifdef USB_DEBUG
+static void ohci_dumpregs(ohci_softc_t *);
+static void ohci_dump_tds(ohci_soft_td_t *);
+static void ohci_dump_td(ohci_soft_td_t *);
+static void ohci_dump_ed(ohci_soft_ed_t *);
+static void ohci_dump_itd(ohci_soft_itd_t *);
+static void ohci_dump_itds(ohci_soft_itd_t *);
+#endif
+
+#define OBARR(sc) bus_space_barrier((sc)->iot, (sc)->ioh, 0, (sc)->sc_size, \
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#define OWRITE1(sc, r, x) \
+ do { OBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); } while (0)
+#define OWRITE2(sc, r, x) \
+ do { OBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); } while (0)
+#define OWRITE4(sc, r, x) \
+ do { OBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); } while (0)
+#define OREAD1(sc, r) (OBARR(sc), bus_space_read_1((sc)->iot, (sc)->ioh, (r)))
+#define OREAD2(sc, r) (OBARR(sc), bus_space_read_2((sc)->iot, (sc)->ioh, (r)))
+#define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r)))
+
+/* Reverse the bits in a value 0 .. 31 */
+static u_int8_t revbits[OHCI_NO_INTRS] =
+ { 0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c,
+ 0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e,
+ 0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d,
+ 0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f };
+
+struct ohci_pipe {
+ struct usbd_pipe pipe;
+ ohci_soft_ed_t *sed;
+ u_int32_t aborting;
+ union {
+ ohci_soft_td_t *td;
+ ohci_soft_itd_t *itd;
+ } tail;
+ /* Info needed for different pipe kinds. */
+ union {
+ /* Control pipe */
+ struct {
+ usb_dma_t reqdma;
+ u_int length;
+ ohci_soft_td_t *setup, *data, *stat;
+ } ctl;
+ /* Interrupt pipe */
+ struct {
+ int nslots;
+ int pos;
+ } intr;
+ /* Bulk pipe */
+ struct {
+ u_int length;
+ int isread;
+ } bulk;
+ /* Iso pipe */
+ struct iso {
+ int next, inuse;
+ } iso;
+ } u;
+};
+
+#define OHCI_INTR_ENDPT 1
+
+static struct usbd_bus_methods ohci_bus_methods = {
+ ohci_open,
+ ohci_softintr,
+ ohci_poll,
+ ohci_allocm,
+ ohci_freem,
+ ohci_allocx,
+ ohci_freex,
+};
+
+static struct usbd_pipe_methods ohci_root_ctrl_methods = {
+ ohci_root_ctrl_transfer,
+ ohci_root_ctrl_start,
+ ohci_root_ctrl_abort,
+ ohci_root_ctrl_close,
+ ohci_noop,
+ ohci_root_ctrl_done,
+};
+
+static struct usbd_pipe_methods ohci_root_intr_methods = {
+ ohci_root_intr_transfer,
+ ohci_root_intr_start,
+ ohci_root_intr_abort,
+ ohci_root_intr_close,
+ ohci_noop,
+ ohci_root_intr_done,
+};
+
+static struct usbd_pipe_methods ohci_device_ctrl_methods = {
+ ohci_device_ctrl_transfer,
+ ohci_device_ctrl_start,
+ ohci_device_ctrl_abort,
+ ohci_device_ctrl_close,
+ ohci_noop,
+ ohci_device_ctrl_done,
+};
+
+static struct usbd_pipe_methods ohci_device_intr_methods = {
+ ohci_device_intr_transfer,
+ ohci_device_intr_start,
+ ohci_device_intr_abort,
+ ohci_device_intr_close,
+ ohci_device_clear_toggle,
+ ohci_device_intr_done,
+};
+
+static struct usbd_pipe_methods ohci_device_bulk_methods = {
+ ohci_device_bulk_transfer,
+ ohci_device_bulk_start,
+ ohci_device_bulk_abort,
+ ohci_device_bulk_close,
+ ohci_device_clear_toggle,
+ ohci_device_bulk_done,
+};
+
+static struct usbd_pipe_methods ohci_device_isoc_methods = {
+ ohci_device_isoc_transfer,
+ ohci_device_isoc_start,
+ ohci_device_isoc_abort,
+ ohci_device_isoc_close,
+ ohci_noop,
+ ohci_device_isoc_done,
+};
+
+int
+ohci_detach(struct ohci_softc *sc, int flags)
+{
+ int i, rv = 0;
+
+ sc->sc_dying = 1;
+ callout_stop(&sc->sc_tmo_rhsc);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ powerhook_disestablish(sc->sc_powerhook);
+ shutdownhook_disestablish(sc->sc_shutdownhook);
+#endif
+
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+
+ usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
+
+ for (i = 0; i < OHCI_NO_EDS; i++)
+ ohci_free_sed(sc, sc->sc_eds[i]);
+ ohci_free_sed(sc, sc->sc_isoc_head);
+ ohci_free_sed(sc, sc->sc_bulk_head);
+ ohci_free_sed(sc, sc->sc_ctrl_head);
+ usb_freemem(&sc->sc_bus, &sc->sc_hccadma);
+
+ return (rv);
+}
+
+ohci_soft_ed_t *
+ohci_alloc_sed(ohci_softc_t *sc)
+{
+ ohci_soft_ed_t *sed;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeeds == NULL) {
+ DPRINTFN(2, ("ohci_alloc_sed: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, OHCI_SED_SIZE * OHCI_SED_CHUNK,
+ OHCI_ED_ALIGN, &dma);
+ if (err)
+ return (NULL);
+ for(i = 0; i < OHCI_SED_CHUNK; i++) {
+ offs = i * OHCI_SED_SIZE;
+ sed = KERNADDR(&dma, offs);
+ sed->physaddr = DMAADDR(&dma, offs);
+ sed->next = sc->sc_freeeds;
+ sc->sc_freeeds = sed;
+ }
+ }
+ sed = sc->sc_freeeds;
+ sc->sc_freeeds = sed->next;
+ memset(&sed->ed, 0, sizeof(ohci_ed_t));
+ sed->next = 0;
+ return (sed);
+}
+
+void
+ohci_free_sed(ohci_softc_t *sc, ohci_soft_ed_t *sed)
+{
+ sed->next = sc->sc_freeeds;
+ sc->sc_freeeds = sed;
+}
+
+ohci_soft_td_t *
+ohci_alloc_std(ohci_softc_t *sc)
+{
+ ohci_soft_td_t *std;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+ int s;
+
+ if (sc->sc_freetds == NULL) {
+ DPRINTFN(2, ("ohci_alloc_std: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, OHCI_STD_SIZE * OHCI_STD_CHUNK,
+ OHCI_TD_ALIGN, &dma);
+ if (err)
+ return (NULL);
+ s = splusb();
+ for(i = 0; i < OHCI_STD_CHUNK; i++) {
+ offs = i * OHCI_STD_SIZE;
+ std = KERNADDR(&dma, offs);
+ std->physaddr = DMAADDR(&dma, offs);
+ std->nexttd = sc->sc_freetds;
+ sc->sc_freetds = std;
+ }
+ splx(s);
+ }
+
+ s = splusb();
+ std = sc->sc_freetds;
+ sc->sc_freetds = std->nexttd;
+ memset(&std->td, 0, sizeof(ohci_td_t));
+ std->nexttd = NULL;
+ std->xfer = NULL;
+ ohci_hash_add_td(sc, std);
+ splx(s);
+
+ return (std);
+}
+
+void
+ohci_free_std(ohci_softc_t *sc, ohci_soft_td_t *std)
+{
+ int s;
+
+ s = splusb();
+ ohci_hash_rem_td(sc, std);
+ std->nexttd = sc->sc_freetds;
+ sc->sc_freetds = std;
+ splx(s);
+}
+
+usbd_status
+ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
+ int alen, int rd, usbd_xfer_handle xfer,
+ ohci_soft_td_t *sp, ohci_soft_td_t **ep)
+{
+ ohci_soft_td_t *next, *cur, *end;
+ ohci_physaddr_t dataphys, physend;
+ u_int32_t tdflags;
+ int offset = 0;
+ int len, maxp, curlen, curlen2, seg, segoff;
+ struct usb_dma_mapping *dma = &xfer->dmamap;
+ u_int16_t flags = xfer->flags;
+
+ DPRINTFN(alen < 4096,("ohci_alloc_std_chain: start len=%d\n", alen));
+
+ len = alen;
+ cur = sp;
+ end = NULL;
+
+ maxp = UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize);
+ tdflags = htole32(
+ (rd ? OHCI_TD_IN : OHCI_TD_OUT) |
+ (flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0) |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_SET_DI(6));
+
+ seg = 0;
+ segoff = 0;
+ while (len > 0) {
+ next = ohci_alloc_std(sc);
+ if (next == NULL)
+ goto nomem;
+
+ /*
+ * The OHCI hardware can handle at most one 4k crossing.
+ * The OHCI spec says: If during the data transfer the buffer
+ * address contained in the HC's working copy of
+ * CurrentBufferPointer crosses a 4K boundary, the upper 20
+ * bits of Buffer End are copied to the working value of
+ * CurrentBufferPointer causing the next buffer address to
+ * be the 0th byte in the same 4K page that contains the
+ * last byte of the buffer (the 4K boundary crossing may
+ * occur within a data packet transfer.)
+ */
+ KASSERT(seg < dma->nsegs, ("ohci_alloc_std_chain: overrun"));
+ dataphys = dma->segs[seg].ds_addr + segoff;
+ curlen = dma->segs[seg].ds_len - segoff;
+ if (curlen > len)
+ curlen = len;
+ physend = dataphys + curlen - 1;
+ if (OHCI_PAGE(dataphys) != OHCI_PAGE(physend)) {
+ /* Truncate to two OHCI pages if there are more. */
+ if (curlen > 2 * OHCI_PAGE_SIZE -
+ OHCI_PAGE_OFFSET(dataphys))
+ curlen = 2 * OHCI_PAGE_SIZE -
+ OHCI_PAGE_OFFSET(dataphys);
+ if (curlen < len)
+ curlen -= curlen % maxp;
+ physend = dataphys + curlen - 1;
+ } else if (OHCI_PAGE_OFFSET(physend + 1) == 0 && curlen < len &&
+ curlen + segoff == dma->segs[seg].ds_len) {
+ /* We can possibly include another segment. */
+ KASSERT(seg + 1 < dma->nsegs,
+ ("ohci_alloc_std_chain: overrun2"));
+ seg++;
+
+ /* Determine how much of the second segment to use. */
+ curlen2 = dma->segs[seg].ds_len;
+ if (curlen + curlen2 > len)
+ curlen2 = len - curlen;
+ if (OHCI_PAGE(dma->segs[seg].ds_addr) !=
+ OHCI_PAGE(dma->segs[seg].ds_addr + curlen2 - 1))
+ curlen2 = OHCI_PAGE_SIZE -
+ OHCI_PAGE_OFFSET(dma->segs[seg].ds_addr);
+ if (curlen + curlen2 < len)
+ curlen2 -= (curlen + curlen2) % maxp;
+
+ if (curlen2 > 0) {
+ /* We can include a second segment */
+ segoff = curlen2;
+ physend = dma->segs[seg].ds_addr + curlen2 - 1;
+ curlen += curlen2;
+ } else {
+ /* Second segment not usable now. */
+ seg--;
+ segoff += curlen;
+ }
+ } else {
+ /* Simple case where there is just one OHCI page. */
+ segoff += curlen;
+ }
+ if (curlen == 0 && len != 0) {
+ /*
+ * A maxp length packet would need to be split.
+ * This shouldn't be possible if PAGE_SIZE >= 4k
+ * and the buffer is contiguous in virtual memory.
+ */
+ panic("ohci_alloc_std_chain: XXX need to copy");
+ }
+ if (segoff >= dma->segs[seg].ds_len) {
+ KASSERT(segoff == dma->segs[seg].ds_len,
+ ("ohci_alloc_std_chain: overlap"));
+ seg++;
+ segoff = 0;
+ }
+ DPRINTFN(4,("ohci_alloc_std_chain: dataphys=0x%08x "
+ "len=%d curlen=%d\n",
+ dataphys, len, curlen));
+ len -= curlen;
+
+ cur->td.td_flags = tdflags;
+ cur->td.td_cbp = htole32(dataphys);
+ cur->nexttd = next;
+ cur->td.td_nexttd = htole32(next->physaddr);
+ cur->td.td_be = htole32(physend);
+ cur->len = curlen;
+ cur->flags = OHCI_ADD_LEN;
+ cur->xfer = xfer;
+ DPRINTFN(10,("ohci_alloc_std_chain: cbp=0x%08x be=0x%08x\n",
+ dataphys, dataphys + curlen - 1));
+ if (len < 0)
+ panic("Length went negative: %d curlen %d dma %p offset %08x", len, curlen, dma, (int)0);
+
+ DPRINTFN(10,("ohci_alloc_std_chain: extend chain\n"));
+ offset += curlen;
+ end = cur;
+ cur = next;
+ }
+ if (((flags & USBD_FORCE_SHORT_XFER) || alen == 0) &&
+ alen % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) {
+ /* Force a 0 length transfer at the end. */
+ next = ohci_alloc_std(sc);
+ if (next == NULL)
+ goto nomem;
+
+ cur->td.td_flags = tdflags;
+ cur->td.td_cbp = 0; /* indicate 0 length packet */
+ cur->nexttd = next;
+ cur->td.td_nexttd = htole32(next->physaddr);
+ cur->td.td_be = ~0;
+ cur->len = 0;
+ cur->flags = 0;
+ cur->xfer = xfer;
+ DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n"));
+ end = cur;
+ }
+ *ep = end;
+
+ return (USBD_NORMAL_COMPLETION);
+
+ nomem:
+ /* XXX free chain */
+ return (USBD_NOMEM);
+}
+
+#if 0
+static void
+ohci_free_std_chain(ohci_softc_t *sc, ohci_soft_td_t *std,
+ ohci_soft_td_t *stdend)
+{
+ ohci_soft_td_t *p;
+
+ for (; std != stdend; std = p) {
+ p = std->nexttd;
+ ohci_free_std(sc, std);
+ }
+}
+#endif
+
+ohci_soft_itd_t *
+ohci_alloc_sitd(ohci_softc_t *sc)
+{
+ ohci_soft_itd_t *sitd;
+ usbd_status err;
+ int i, s, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeitds == NULL) {
+ DPRINTFN(2, ("ohci_alloc_sitd: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, OHCI_SITD_SIZE * OHCI_SITD_CHUNK,
+ OHCI_ITD_ALIGN, &dma);
+ if (err)
+ return (NULL);
+ s = splusb();
+ for(i = 0; i < OHCI_SITD_CHUNK; i++) {
+ offs = i * OHCI_SITD_SIZE;
+ sitd = KERNADDR(&dma, offs);
+ sitd->physaddr = DMAADDR(&dma, offs);
+ sitd->nextitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd;
+ }
+ splx(s);
+ }
+
+ s = splusb();
+ sitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd->nextitd;
+ memset(&sitd->itd, 0, sizeof(ohci_itd_t));
+ sitd->nextitd = NULL;
+ sitd->xfer = NULL;
+ ohci_hash_add_itd(sc, sitd);
+ splx(s);
+
+#ifdef DIAGNOSTIC
+ sitd->isdone = 0;
+#endif
+
+ return (sitd);
+}
+
+void
+ohci_free_sitd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+{
+ int s;
+
+ DPRINTFN(10,("ohci_free_sitd: sitd=%p\n", sitd));
+
+#ifdef DIAGNOSTIC
+ if (!sitd->isdone) {
+ panic("ohci_free_sitd: sitd=%p not done", sitd);
+ return;
+ }
+ /* Warn double free */
+ sitd->isdone = 0;
+#endif
+
+ s = splusb();
+ ohci_hash_rem_itd(sc, sitd);
+ sitd->nextitd = sc->sc_freeitds;
+ sc->sc_freeitds = sitd;
+ splx(s);
+}
+
+usbd_status
+ohci_init(ohci_softc_t *sc)
+{
+ ohci_soft_ed_t *sed, *psed;
+ usbd_status err;
+ int i;
+ u_int32_t rev;
+
+ DPRINTF(("ohci_init: start\n"));
+ printf("%s:", device_get_nameunit(sc->sc_bus.bdev));
+ rev = OREAD4(sc, OHCI_REVISION);
+ printf(" OHCI version %d.%d%s\n", OHCI_REV_HI(rev), OHCI_REV_LO(rev),
+ OHCI_REV_LEGACY(rev) ? ", legacy support" : "");
+
+ if (OHCI_REV_HI(rev) != 1 || OHCI_REV_LO(rev) != 0) {
+ printf("%s: unsupported OHCI revision\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ return (USBD_INVAL);
+ }
+ sc->sc_bus.usbrev = USBREV_1_0;
+
+ for (i = 0; i < OHCI_HASH_SIZE; i++)
+ LIST_INIT(&sc->sc_hash_tds[i]);
+ for (i = 0; i < OHCI_HASH_SIZE; i++)
+ LIST_INIT(&sc->sc_hash_itds[i]);
+
+ STAILQ_INIT(&sc->sc_free_xfers);
+
+ /* XXX determine alignment by R/W */
+ /* Allocate the HCCA area. */
+ err = usb_allocmem(&sc->sc_bus, OHCI_HCCA_SIZE,
+ OHCI_HCCA_ALIGN, &sc->sc_hccadma);
+ if (err)
+ return (err);
+ sc->sc_hcca = KERNADDR(&sc->sc_hccadma, 0);
+ memset(sc->sc_hcca, 0, OHCI_HCCA_SIZE);
+
+ sc->sc_eintrs = OHCI_NORMAL_INTRS;
+
+ /* Allocate dummy ED that starts the control list. */
+ sc->sc_ctrl_head = ohci_alloc_sed(sc);
+ if (sc->sc_ctrl_head == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ sc->sc_ctrl_head->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+
+ /* Allocate dummy ED that starts the bulk list. */
+ sc->sc_bulk_head = ohci_alloc_sed(sc);
+ if (sc->sc_bulk_head == NULL) {
+ err = USBD_NOMEM;
+ goto bad2;
+ }
+ sc->sc_bulk_head->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+
+ /* Allocate dummy ED that starts the isochronous list. */
+ sc->sc_isoc_head = ohci_alloc_sed(sc);
+ if (sc->sc_isoc_head == NULL) {
+ err = USBD_NOMEM;
+ goto bad3;
+ }
+ sc->sc_isoc_head->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+
+ /* Allocate all the dummy EDs that make up the interrupt tree. */
+ for (i = 0; i < OHCI_NO_EDS; i++) {
+ sed = ohci_alloc_sed(sc);
+ if (sed == NULL) {
+ while (--i >= 0)
+ ohci_free_sed(sc, sc->sc_eds[i]);
+ err = USBD_NOMEM;
+ goto bad4;
+ }
+ /* All ED fields are set to 0. */
+ sc->sc_eds[i] = sed;
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+ if (i != 0)
+ psed = sc->sc_eds[(i-1) / 2];
+ else
+ psed= sc->sc_isoc_head;
+ sed->next = psed;
+ sed->ed.ed_nexted = htole32(psed->physaddr);
+ }
+ /*
+ * Fill HCCA interrupt table. The bit reversal is to get
+ * the tree set up properly to spread the interrupts.
+ */
+ for (i = 0; i < OHCI_NO_INTRS; i++)
+ sc->sc_hcca->hcca_interrupt_table[revbits[i]] =
+ htole32(sc->sc_eds[OHCI_NO_EDS-OHCI_NO_INTRS+i]->physaddr);
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 15) {
+ for (i = 0; i < OHCI_NO_EDS; i++) {
+ printf("ed#%d ", i);
+ ohci_dump_ed(sc->sc_eds[i]);
+ }
+ printf("iso ");
+ ohci_dump_ed(sc->sc_isoc_head);
+ }
+#endif
+
+ err = ohci_controller_init(sc);
+ if (err != USBD_NORMAL_COMPLETION)
+ goto bad5;
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &ohci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct ohci_pipe);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sc->sc_powerhook = powerhook_establish(ohci_power, sc);
+ sc->sc_shutdownhook = shutdownhook_establish(ohci_shutdown, sc);
+#endif
+
+ callout_init(&sc->sc_tmo_rhsc, 0);
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad5:
+ for (i = 0; i < OHCI_NO_EDS; i++)
+ ohci_free_sed(sc, sc->sc_eds[i]);
+ bad4:
+ ohci_free_sed(sc, sc->sc_isoc_head);
+ bad3:
+ ohci_free_sed(sc, sc->sc_bulk_head);
+ bad2:
+ ohci_free_sed(sc, sc->sc_ctrl_head);
+ bad1:
+ usb_freemem(&sc->sc_bus, &sc->sc_hccadma);
+ return (err);
+}
+
+static usbd_status
+ohci_controller_init(ohci_softc_t *sc)
+{
+ int i;
+ u_int32_t ctl, ival, hcr, fm, per, desca;
+
+ /* Determine in what context we are running. */
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ if (ctl & OHCI_IR) {
+ /* SMM active, request change */
+ DPRINTF(("ohci_init: SMM active, request owner change\n"));
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_OCR);
+ for (i = 0; i < 100 && (ctl & OHCI_IR); i++) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ }
+ if (ctl & OHCI_IR) {
+ printf("%s: SMM does not respond, resetting\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ goto reset;
+ }
+#if 0
+/* Don't bother trying to reuse the BIOS init, we'll reset it anyway. */
+ } else if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_RESET) {
+ /* BIOS started controller. */
+ DPRINTF(("ohci_init: BIOS active\n"));
+ if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_OPERATIONAL) {
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_OPERATIONAL);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
+ }
+#endif
+ } else {
+ DPRINTF(("ohci_init: cold started\n"));
+ reset:
+ /* Controller was cold started. */
+ usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY);
+ }
+
+ /*
+ * This reset should not be necessary according to the OHCI spec, but
+ * without it some controllers do not start.
+ */
+ DPRINTF(("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY);
+
+ /* We now own the host controller and the bus has been reset. */
+ ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL));
+
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */
+ /* Nominal time for a reset is 10 us. */
+ for (i = 0; i < 10; i++) {
+ delay(10);
+ hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR;
+ if (!hcr)
+ break;
+ }
+ if (hcr) {
+ printf("%s: reset timeout\n", device_get_nameunit(sc->sc_bus.bdev));
+ return (USBD_IOERROR);
+ }
+#ifdef USB_DEBUG
+ if (ohcidebug > 15)
+ ohci_dumpregs(sc);
+#endif
+
+ /* The controller is now in SUSPEND state, we have 2ms to finish. */
+
+ /* Set up HC registers. */
+ OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0));
+ OWRITE4(sc, OHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr);
+ OWRITE4(sc, OHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr);
+ /* disable all interrupts and then switch on all desired interrupts */
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE);
+ /* switch on desired functional features */
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR);
+ ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE |
+ OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL;
+ /* And finally start it! */
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+
+ /*
+ * The controller is now OPERATIONAL. Set a some final
+ * registers that should be set earlier, but that the
+ * controller ignores when in the SUSPEND state.
+ */
+ fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT;
+ fm |= OHCI_FSMPS(ival) | ival;
+ OWRITE4(sc, OHCI_FM_INTERVAL, fm);
+ per = OHCI_PERIODIC(ival); /* 90% periodic */
+ OWRITE4(sc, OHCI_PERIODIC_START, per);
+
+ /* Fiddle the No OverCurrent Protection bit to avoid chip bug. */
+ desca = OREAD4(sc, OHCI_RH_DESCRIPTOR_A);
+ OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca | OHCI_NOCP);
+ OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */
+ usb_delay_ms(&sc->sc_bus, OHCI_ENABLE_POWER_DELAY);
+ OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca);
+
+ /*
+ * The AMD756 requires a delay before re-reading the register,
+ * otherwise it will occasionally report 0 ports.
+ */
+ sc->sc_noport = 0;
+ for (i = 0; i < 10 && sc->sc_noport == 0; i++) {
+ usb_delay_ms(&sc->sc_bus, OHCI_READ_DESC_DELAY);
+ sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A));
+ }
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5)
+ ohci_dumpregs(sc);
+#endif
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+ohci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
+{
+ return (usb_allocmem(bus, size, 0, dma));
+}
+
+void
+ohci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ usb_freemem(bus, dma);
+}
+
+usbd_xfer_handle
+ohci_allocx(struct usbd_bus *bus)
+{
+ struct ohci_softc *sc = (struct ohci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ xfer = STAILQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_FREE) {
+ printf("ohci_allocx: xfer=%p not free, 0x%08x\n", xfer,
+ xfer->busy_free);
+ }
+#endif
+ } else {
+ xfer = malloc(sizeof(struct ohci_xfer), M_USB, M_NOWAIT);
+ }
+ if (xfer != NULL) {
+ memset(xfer, 0, sizeof (struct ohci_xfer));
+ usb_init_task(&OXFER(xfer)->abort_task, ohci_timeout_task,
+ xfer);
+ OXFER(xfer)->ohci_xfer_flags = 0;
+#ifdef DIAGNOSTIC
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+ return (xfer);
+}
+
+void
+ohci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct ohci_softc *sc = (struct ohci_softc *)bus;
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("ohci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+ xfer->busy_free);
+ return;
+ }
+ xfer->busy_free = XFER_FREE;
+#endif
+ STAILQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+/*
+ * Shut down the controller when the system is going down.
+ */
+void
+ohci_shutdown(void *v)
+{
+ ohci_softc_t *sc = v;
+
+ DPRINTF(("ohci_shutdown: stopping the HC\n"));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+}
+
+/*
+ * Handle suspend/resume.
+ *
+ * We need to switch to polling mode here, because this routine is
+ * called from an intterupt context. This is all right since we
+ * are almost suspended anyway.
+ */
+void
+ohci_power(int why, void *v)
+{
+ ohci_softc_t *sc = v;
+ u_int32_t ctl;
+ int s;
+
+#ifdef USB_DEBUG
+ DPRINTF(("ohci_power: sc=%p, why=%d\n", sc, why));
+ ohci_dumpregs(sc);
+#endif
+
+ s = splhardusb();
+ if (why != PWR_RESUME) {
+ sc->sc_bus.use_polling++;
+ ctl = OREAD4(sc, OHCI_CONTROL) & ~OHCI_HCFS_MASK;
+ if (sc->sc_control == 0) {
+ /*
+ * Preserve register values, in case that APM BIOS
+ * does not recover them.
+ */
+ sc->sc_control = ctl;
+ sc->sc_intre = OREAD4(sc, OHCI_INTERRUPT_ENABLE);
+ }
+ ctl |= OHCI_HCFS_SUSPEND;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+ sc->sc_bus.use_polling--;
+ } else {
+ sc->sc_bus.use_polling++;
+
+ /* Some broken BIOSes never initialize Controller chip */
+ ohci_controller_init(sc);
+
+ if (sc->sc_intre)
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE,
+ sc->sc_intre & (OHCI_ALL_INTRS | OHCI_MIE));
+ if (sc->sc_control)
+ ctl = sc->sc_control;
+ else
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ ctl |= OHCI_HCFS_RESUME;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
+ ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY);
+ sc->sc_control = sc->sc_intre = 0;
+ sc->sc_bus.use_polling--;
+ }
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+void
+ohci_dumpregs(ohci_softc_t *sc)
+{
+ DPRINTF(("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n",
+ OREAD4(sc, OHCI_REVISION),
+ OREAD4(sc, OHCI_CONTROL),
+ OREAD4(sc, OHCI_COMMAND_STATUS)));
+ DPRINTF((" intrstat=0x%08x intre=0x%08x intrd=0x%08x\n",
+ OREAD4(sc, OHCI_INTERRUPT_STATUS),
+ OREAD4(sc, OHCI_INTERRUPT_ENABLE),
+ OREAD4(sc, OHCI_INTERRUPT_DISABLE)));
+ DPRINTF((" hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n",
+ OREAD4(sc, OHCI_HCCA),
+ OREAD4(sc, OHCI_PERIOD_CURRENT_ED),
+ OREAD4(sc, OHCI_CONTROL_HEAD_ED)));
+ DPRINTF((" ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n",
+ OREAD4(sc, OHCI_CONTROL_CURRENT_ED),
+ OREAD4(sc, OHCI_BULK_HEAD_ED),
+ OREAD4(sc, OHCI_BULK_CURRENT_ED)));
+ DPRINTF((" done=0x%08x fmival=0x%08x fmrem=0x%08x\n",
+ OREAD4(sc, OHCI_DONE_HEAD),
+ OREAD4(sc, OHCI_FM_INTERVAL),
+ OREAD4(sc, OHCI_FM_REMAINING)));
+ DPRINTF((" fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n",
+ OREAD4(sc, OHCI_FM_NUMBER),
+ OREAD4(sc, OHCI_PERIODIC_START),
+ OREAD4(sc, OHCI_LS_THRESHOLD)));
+ DPRINTF((" desca=0x%08x descb=0x%08x stat=0x%08x\n",
+ OREAD4(sc, OHCI_RH_DESCRIPTOR_A),
+ OREAD4(sc, OHCI_RH_DESCRIPTOR_B),
+ OREAD4(sc, OHCI_RH_STATUS)));
+ DPRINTF((" port1=0x%08x port2=0x%08x\n",
+ OREAD4(sc, OHCI_RH_PORT_STATUS(1)),
+ OREAD4(sc, OHCI_RH_PORT_STATUS(2))));
+ DPRINTF((" HCCA: frame_number=0x%04x done_head=0x%08x\n",
+ le32toh(sc->sc_hcca->hcca_frame_number),
+ le32toh(sc->sc_hcca->hcca_done_head)));
+}
+#endif
+
+static int ohci_intr1(ohci_softc_t *);
+
+void
+ohci_intr(void *p)
+{
+ ohci_softc_t *sc = p;
+
+ if (sc == NULL || sc->sc_dying)
+ return;
+
+ /* If we get an interrupt while polling, then just ignore it. */
+ if (sc->sc_bus.use_polling) {
+#ifdef DIAGNOSTIC
+ printf("ohci_intr: ignored interrupt while polling\n");
+#endif
+ return;
+ }
+
+ ohci_intr1(sc);
+}
+
+static int
+ohci_intr1(ohci_softc_t *sc)
+{
+ u_int32_t intrs, eintrs;
+ ohci_physaddr_t done;
+
+ DPRINTFN(14,("ohci_intr1: enter\n"));
+
+ /* In case the interrupt occurs before initialization has completed. */
+ if (sc == NULL || sc->sc_hcca == NULL) {
+#ifdef DIAGNOSTIC
+ printf("ohci_intr: sc->sc_hcca == NULL\n");
+#endif
+ return (0);
+ }
+
+ intrs = 0;
+ done = le32toh(sc->sc_hcca->hcca_done_head);
+
+ /* The LSb of done is used to inform the HC Driver that an interrupt
+ * condition exists for both the Done list and for another event
+ * recorded in HcInterruptStatus. On an interrupt from the HC, the HC
+ * Driver checks the HccaDoneHead Value. If this value is 0, then the
+ * interrupt was caused by other than the HccaDoneHead update and the
+ * HcInterruptStatus register needs to be accessed to determine that
+ * exact interrupt cause. If HccaDoneHead is nonzero, then a Done list
+ * update interrupt is indicated and if the LSb of done is nonzero,
+ * then an additional interrupt event is indicated and
+ * HcInterruptStatus should be checked to determine its cause.
+ */
+ if (done != 0) {
+ if (done & ~OHCI_DONE_INTRS)
+ intrs = OHCI_WDH;
+ if (done & OHCI_DONE_INTRS) {
+ intrs |= OREAD4(sc, OHCI_INTERRUPT_STATUS);
+ done &= ~OHCI_DONE_INTRS;
+ }
+ sc->sc_hcca->hcca_done_head = 0;
+ } else
+ intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH;
+
+ if (intrs == 0) /* nothing to be done (PCI shared interrupt) */
+ return (0);
+
+ intrs &= ~OHCI_MIE;
+ OWRITE4(sc, OHCI_INTERRUPT_STATUS, intrs); /* Acknowledge */
+ eintrs = intrs & sc->sc_eintrs;
+ if (!eintrs)
+ return (0);
+
+ sc->sc_bus.intr_context++;
+ sc->sc_bus.no_intrs++;
+ DPRINTFN(7, ("ohci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n",
+ sc, (u_int)intrs, OREAD4(sc, OHCI_INTERRUPT_STATUS),
+ (u_int)eintrs));
+
+ if (eintrs & OHCI_SO) {
+ sc->sc_overrun_cnt++;
+ if (usbd_ratecheck(&sc->sc_overrun_ntc)) {
+ printf("%s: %u scheduling overruns\n",
+ device_get_nameunit(sc->sc_bus.bdev), sc->sc_overrun_cnt);
+ sc->sc_overrun_cnt = 0;
+ }
+ /* XXX do what */
+ eintrs &= ~OHCI_SO;
+ }
+ if (eintrs & OHCI_WDH) {
+ ohci_add_done(sc, done &~ OHCI_DONE_INTRS);
+ usb_schedsoftintr(&sc->sc_bus);
+ eintrs &= ~OHCI_WDH;
+ }
+ if (eintrs & OHCI_RD) {
+ printf("%s: resume detect\n", device_get_nameunit(sc->sc_bus.bdev));
+ /* XXX process resume detect */
+ }
+ if (eintrs & OHCI_UE) {
+ printf("%s: unrecoverable error, controller halted\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ /* XXX what else */
+ }
+ if (eintrs & OHCI_RHSC) {
+ ohci_rhsc(sc, sc->sc_intrxfer);
+ /*
+ * Disable RHSC interrupt for now, because it will be
+ * on until the port has been reset.
+ */
+ ohci_rhsc_able(sc, 0);
+ /* Do not allow RHSC interrupts > 1 per second */
+ callout_reset(&sc->sc_tmo_rhsc, hz, ohci_rhsc_enable, sc);
+ eintrs &= ~OHCI_RHSC;
+ }
+
+ sc->sc_bus.intr_context--;
+
+ if (eintrs != 0) {
+ /* Block unprocessed interrupts. XXX */
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, eintrs);
+ sc->sc_eintrs &= ~eintrs;
+ printf("%s: blocking intrs 0x%x\n",
+ device_get_nameunit(sc->sc_bus.bdev), eintrs);
+ }
+
+ return (1);
+}
+
+void
+ohci_rhsc_able(ohci_softc_t *sc, int on)
+{
+ DPRINTFN(4, ("ohci_rhsc_able: on=%d\n", on));
+ if (on) {
+ sc->sc_eintrs |= OHCI_RHSC;
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC);
+ } else {
+ sc->sc_eintrs &= ~OHCI_RHSC;
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC);
+ }
+}
+
+void
+ohci_rhsc_enable(void *v_sc)
+{
+ ohci_softc_t *sc = v_sc;
+ int s;
+
+ s = splhardusb();
+ ohci_rhsc_able(sc, 1);
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+char *ohci_cc_strs[] = {
+ "NO_ERROR",
+ "CRC",
+ "BIT_STUFFING",
+ "DATA_TOGGLE_MISMATCH",
+ "STALL",
+ "DEVICE_NOT_RESPONDING",
+ "PID_CHECK_FAILURE",
+ "UNEXPECTED_PID",
+ "DATA_OVERRUN",
+ "DATA_UNDERRUN",
+ "BUFFER_OVERRUN",
+ "BUFFER_UNDERRUN",
+ "reserved",
+ "reserved",
+ "NOT_ACCESSED",
+ "NOT_ACCESSED"
+};
+#endif
+
+void
+ohci_add_done(ohci_softc_t *sc, ohci_physaddr_t done)
+{
+ ohci_soft_itd_t *sitd, *sidone, **ip;
+ ohci_soft_td_t *std, *sdone, **p;
+
+ /* Reverse the done list. */
+ for (sdone = NULL, sidone = NULL; done != 0; ) {
+ std = ohci_hash_find_td(sc, done);
+ if (std != NULL) {
+ std->dnext = sdone;
+ done = le32toh(std->td.td_nexttd);
+ sdone = std;
+ DPRINTFN(10,("add TD %p\n", std));
+ continue;
+ }
+ sitd = ohci_hash_find_itd(sc, done);
+ if (sitd != NULL) {
+ sitd->dnext = sidone;
+ done = le32toh(sitd->itd.itd_nextitd);
+ sidone = sitd;
+ DPRINTFN(5,("add ITD %p\n", sitd));
+ continue;
+ }
+ panic("ohci_add_done: addr 0x%08lx not found", (u_long)done);
+ }
+
+ /* sdone & sidone now hold the done lists. */
+ /* Put them on the already processed lists. */
+ for (p = &sc->sc_sdone; *p != NULL; p = &(*p)->dnext)
+ ;
+ *p = sdone;
+ for (ip = &sc->sc_sidone; *ip != NULL; ip = &(*ip)->dnext)
+ ;
+ *ip = sidone;
+}
+
+void
+ohci_softintr(void *v)
+{
+ ohci_softc_t *sc = v;
+ ohci_soft_itd_t *sitd, *sidone, *sitdnext;
+ ohci_soft_td_t *std, *sdone, *stdnext, *p, *n;
+ usbd_xfer_handle xfer;
+ struct ohci_pipe *opipe;
+ int len, cc, s;
+ int i, j, iframes;
+
+ DPRINTFN(10,("ohci_softintr: enter\n"));
+
+ sc->sc_bus.intr_context++;
+
+ s = splhardusb();
+ sdone = sc->sc_sdone;
+ sc->sc_sdone = NULL;
+ sidone = sc->sc_sidone;
+ sc->sc_sidone = NULL;
+ splx(s);
+
+ DPRINTFN(10,("ohci_softintr: sdone=%p sidone=%p\n", sdone, sidone));
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 10) {
+ DPRINTF(("ohci_process_done: TD done:\n"));
+ ohci_dump_tds(sdone);
+ }
+#endif
+
+ for (std = sdone; std; std = stdnext) {
+ xfer = std->xfer;
+ stdnext = std->dnext;
+ DPRINTFN(10, ("ohci_process_done: std=%p xfer=%p hcpriv=%p\n",
+ std, xfer, (xfer ? xfer->hcpriv : NULL)));
+ if (xfer == NULL) {
+ /*
+ * xfer == NULL: There seems to be no xfer associated
+ * with this TD. It is tailp that happened to end up on
+ * the done queue.
+ */
+ continue;
+ }
+ if (xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("ohci_process_done: cancel/timeout %p\n",
+ xfer));
+ /* Handled by abort routine. */
+ continue;
+ }
+
+ len = std->len;
+ if (std->td.td_cbp != 0)
+ len -= le32toh(std->td.td_be) -
+ le32toh(std->td.td_cbp) + 1;
+ DPRINTFN(10, ("ohci_process_done: len=%d, flags=0x%x\n", len,
+ std->flags));
+ if (std->flags & OHCI_ADD_LEN)
+ xfer->actlen += len;
+
+ cc = OHCI_TD_GET_CC(le32toh(std->td.td_flags));
+ if (cc != OHCI_CC_NO_ERROR) {
+ /*
+ * Endpoint is halted. First unlink all the TDs
+ * belonging to the failed transfer, and then restart
+ * the endpoint.
+ */
+ opipe = (struct ohci_pipe *)xfer->pipe;
+
+ DPRINTFN(15,("ohci_process_done: error cc=%d (%s)\n",
+ OHCI_TD_GET_CC(le32toh(std->td.td_flags)),
+ ohci_cc_strs[OHCI_TD_GET_CC(le32toh(std->td.td_flags))]));
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(OXFER(xfer)->xfer.pipe->device,
+ &OXFER(xfer)->abort_task);
+
+ /* Remove all this xfer's TDs from the done queue. */
+ for (p = std; p->dnext != NULL; p = p->dnext) {
+ if (p->dnext->xfer != xfer)
+ continue;
+ p->dnext = p->dnext->dnext;
+ }
+ /* The next TD may have been removed. */
+ stdnext = std->dnext;
+
+ /* Remove all TDs belonging to this xfer. */
+ for (p = xfer->hcpriv; p->xfer == xfer; p = n) {
+ n = p->nexttd;
+ ohci_free_std(sc, p);
+ }
+
+ /* clear halt */
+ opipe->sed->ed.ed_headp = htole32(p->physaddr);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+
+ if (cc == OHCI_CC_STALL)
+ xfer->status = USBD_STALLED;
+ else
+ xfer->status = USBD_IOERROR;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ continue;
+ }
+ /*
+ * Skip intermediate TDs. They remain linked from
+ * xfer->hcpriv and we free them when the transfer completes.
+ */
+ if ((std->flags & OHCI_CALL_DONE) == 0)
+ continue;
+
+ /* Normal transfer completion */
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(OXFER(xfer)->xfer.pipe->device,
+ &OXFER(xfer)->abort_task);
+ for (p = xfer->hcpriv; p->xfer == xfer; p = n) {
+ n = p->nexttd;
+ ohci_free_std(sc, p);
+ }
+ xfer->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ }
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 10) {
+ DPRINTF(("ohci_softintr: ITD done:\n"));
+ ohci_dump_itds(sidone);
+ }
+#endif
+
+ for (sitd = sidone; sitd != NULL; sitd = sitdnext) {
+ xfer = sitd->xfer;
+ sitdnext = sitd->dnext;
+ sitd->flags |= OHCI_ITD_INTFIN;
+ DPRINTFN(1, ("ohci_process_done: sitd=%p xfer=%p hcpriv=%p\n",
+ sitd, xfer, xfer ? xfer->hcpriv : 0));
+ if (xfer == NULL)
+ continue;
+ if (xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("ohci_process_done: cancel/timeout %p\n",
+ xfer));
+ /* Handled by abort routine. */
+ continue;
+ }
+ if (xfer->pipe)
+ if (xfer->pipe->aborting)
+ continue; /*Ignore.*/
+#ifdef DIAGNOSTIC
+ if (sitd->isdone)
+ printf("ohci_softintr: sitd=%p is done\n", sitd);
+ sitd->isdone = 1;
+#endif
+ opipe = (struct ohci_pipe *)xfer->pipe;
+ if (opipe->aborting)
+ continue;
+
+ if (sitd->flags & OHCI_CALL_DONE) {
+ ohci_soft_itd_t *next;
+
+ opipe->u.iso.inuse -= xfer->nframes;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ for (i = 0, sitd = xfer->hcpriv;;sitd = next) {
+ next = sitd->nextitd;
+ if (OHCI_ITD_GET_CC(sitd->itd.itd_flags) != OHCI_CC_NO_ERROR)
+ xfer->status = USBD_IOERROR;
+
+ if (xfer->status == USBD_NORMAL_COMPLETION) {
+ iframes = OHCI_ITD_GET_FC(sitd->itd.itd_flags);
+ for (j = 0; j < iframes; i++, j++) {
+ len = le16toh(sitd->itd.itd_offset[j]);
+ len =
+ (OHCI_ITD_PSW_GET_CC(len) ==
+ OHCI_CC_NOT_ACCESSED) ? 0 :
+ OHCI_ITD_PSW_LENGTH(len);
+ xfer->frlengths[i] = len;
+ }
+ }
+ if (sitd->flags & OHCI_CALL_DONE)
+ break;
+ }
+ for (sitd = xfer->hcpriv; sitd->xfer == xfer;
+ sitd = next) {
+ next = sitd->nextitd;
+ ohci_free_sitd(sc, sitd);
+ }
+
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ }
+ }
+
+#ifdef USB_USE_SOFTINTR
+ if (sc->sc_softwake) {
+ sc->sc_softwake = 0;
+ wakeup(&sc->sc_softwake);
+ }
+#endif /* USB_USE_SOFTINTR */
+
+ sc->sc_bus.intr_context--;
+ DPRINTFN(10,("ohci_softintr: done:\n"));
+}
+
+void
+ohci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ DPRINTFN(10,("ohci_device_ctrl_done: xfer=%p\n", xfer));
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ panic("ohci_device_ctrl_done: not a request");
+ }
+#endif
+ xfer->hcpriv = NULL;
+}
+
+void
+ohci_device_intr_done(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ usbd_status err;
+
+ DPRINTFN(10,("ohci_device_intr_done: xfer=%p, actlen=%d\n",
+ xfer, xfer->actlen));
+
+ xfer->hcpriv = NULL;
+ if (xfer->pipe->repeat) {
+ err = ohci_device_intr_insert(sc, xfer);
+ if (err) {
+ xfer->status = err;
+ return;
+ }
+ }
+}
+
+void
+ohci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ DPRINTFN(10,("ohci_device_bulk_done: xfer=%p, actlen=%d\n",
+ xfer, xfer->actlen));
+
+ xfer->hcpriv = NULL;
+}
+
+/*
+ * XXX write back xfer data for architectures with a write-back
+ * data cache; this is a hack because usb is mis-architected
+ * in blindly mixing bus_dma w/ PIO.
+ */
+static __inline void
+hacksync(usbd_xfer_handle xfer)
+{
+ bus_dma_tag_t tag;
+ struct usb_dma_mapping *dmap;
+
+ if (xfer->length == 0)
+ return;
+ tag = xfer->pipe->device->bus->buffer_dmatag;
+ dmap = &xfer->dmamap;
+ bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREWRITE);
+}
+
+void
+ohci_rhsc(ohci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe;
+ u_char *p;
+ int i, m;
+ int hstatus;
+
+ hstatus = OREAD4(sc, OHCI_RH_STATUS);
+ DPRINTF(("ohci_rhsc: sc=%p xfer=%p hstatus=0x%08x\n",
+ sc, xfer, hstatus));
+
+ if (xfer == NULL) {
+ /* Just ignore the change. */
+ return;
+ }
+
+ pipe = xfer->pipe;
+
+ p = xfer->buffer;
+ m = min(sc->sc_noport, xfer->length * 8 - 1);
+ memset(p, 0, xfer->length);
+ for (i = 1; i <= m; i++) {
+ /* Pick out CHANGE bits from the status reg. */
+ if (OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16)
+ p[i/8] |= 1 << (i%8);
+ }
+ DPRINTF(("ohci_rhsc: change=0x%02x\n", *p));
+ xfer->actlen = xfer->length;
+ xfer->status = USBD_NORMAL_COMPLETION;
+
+ hacksync(xfer); /* XXX to compensate for usb_transfer_complete */
+ usb_transfer_complete(xfer);
+}
+
+void
+ohci_root_intr_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+void
+ohci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+ xfer->hcpriv = NULL;
+}
+
+/*
+ * Wait here until controller claims to have an interrupt.
+ * Then call ohci_intr and return. Use timeout to avoid waiting
+ * too long.
+ */
+void
+ohci_waitintr(ohci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ int timo = xfer->timeout;
+ int usecs;
+ u_int32_t intrs;
+
+ xfer->status = USBD_IN_PROGRESS;
+ for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ if (sc->sc_dying)
+ break;
+ intrs = OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs;
+ DPRINTFN(15,("ohci_waitintr: 0x%04x\n", intrs));
+#ifdef USB_DEBUG
+ if (ohcidebug > 15)
+ ohci_dumpregs(sc);
+#endif
+ if (intrs) {
+ ohci_intr1(sc);
+ if (xfer->status != USBD_IN_PROGRESS)
+ return;
+ }
+ }
+
+ /* Timeout */
+ DPRINTF(("ohci_waitintr: timeout\n"));
+ xfer->status = USBD_TIMEOUT;
+ usb_transfer_complete(xfer);
+ /* XXX should free TD */
+}
+
+void
+ohci_poll(struct usbd_bus *bus)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)bus;
+#ifdef USB_DEBUG
+ static int last;
+ int new;
+ new = OREAD4(sc, OHCI_INTERRUPT_STATUS);
+ if (new != last) {
+ DPRINTFN(10,("ohci_poll: intrs=0x%04x\n", new));
+ last = new;
+ }
+#endif
+
+ if (OREAD4(sc, OHCI_INTERRUPT_STATUS) & sc->sc_eintrs)
+ ohci_intr1(sc);
+}
+
+usbd_status
+ohci_device_request(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usb_device_request_t *req = &xfer->request;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ ohci_soft_td_t *setup, *stat, *next, *tail;
+ ohci_soft_ed_t *sed;
+ int isread;
+ int len;
+ usbd_status err;
+ int s;
+
+ isread = req->bmRequestType & UT_READ;
+ len = UGETW(req->wLength);
+
+ DPRINTFN(3,("ohci_device_control type=0x%02x, request=0x%02x, "
+ "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n",
+ req->bmRequestType, req->bRequest, UGETW(req->wValue),
+ UGETW(req->wIndex), len, dev->address,
+ opipe->pipe.endpoint->edesc->bEndpointAddress));
+
+ setup = opipe->tail.td;
+ stat = ohci_alloc_std(sc);
+ if (stat == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ tail = ohci_alloc_std(sc);
+ if (tail == NULL) {
+ err = USBD_NOMEM;
+ goto bad2;
+ }
+ tail->xfer = NULL;
+
+ sed = opipe->sed;
+ opipe->u.ctl.length = len;
+ next = stat;
+
+ /* Set up data transaction */
+ if (len != 0) {
+ ohci_soft_td_t *std = stat;
+
+ err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
+ std, &stat);
+ stat = stat->nexttd; /* point at free TD */
+ if (err)
+ goto bad3;
+ /* Start toggle at 1 and then use the carried toggle. */
+ std->td.td_flags &= htole32(~OHCI_TD_TOGGLE_MASK);
+ std->td.td_flags |= htole32(OHCI_TD_TOGGLE_1);
+ }
+
+ memcpy(KERNADDR(&opipe->u.ctl.reqdma, 0), req, sizeof *req);
+
+ setup->td.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC |
+ OHCI_TD_TOGGLE_0 | OHCI_TD_SET_DI(6));
+ setup->td.td_cbp = htole32(DMAADDR(&opipe->u.ctl.reqdma, 0));
+ setup->nexttd = next;
+ setup->td.td_nexttd = htole32(next->physaddr);
+ setup->td.td_be = htole32(le32toh(setup->td.td_cbp) + sizeof *req - 1);
+ setup->len = 0;
+ setup->xfer = xfer;
+ setup->flags = 0;
+ xfer->hcpriv = setup;
+
+ stat->td.td_flags = htole32(
+ (isread ? OHCI_TD_OUT : OHCI_TD_IN) |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1));
+ stat->td.td_cbp = 0;
+ stat->nexttd = tail;
+ stat->td.td_nexttd = htole32(tail->physaddr);
+ stat->td.td_be = 0;
+ stat->flags = OHCI_CALL_DONE;
+ stat->len = 0;
+ stat->xfer = xfer;
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ DPRINTF(("ohci_device_request:\n"));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(setup);
+ }
+#endif
+
+ /* Insert ED in schedule */
+ s = splusb();
+ sed->ed.ed_tailp = htole32(tail->physaddr);
+ opipe->tail.td = tail;
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ohci_timeout, xfer);
+ }
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 20) {
+ delay(10000);
+ DPRINTF(("ohci_device_request: status=%x\n",
+ OREAD4(sc, OHCI_COMMAND_STATUS)));
+ ohci_dumpregs(sc);
+ printf("ctrl head:\n");
+ ohci_dump_ed(sc->sc_ctrl_head);
+ printf("sed:\n");
+ ohci_dump_ed(sed);
+ ohci_dump_tds(setup);
+ }
+#endif
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad3:
+ ohci_free_std(sc, tail);
+ bad2:
+ ohci_free_std(sc, stat);
+ bad1:
+ return (err);
+}
+
+/*
+ * Add an ED to the schedule. Called at splusb().
+ */
+void
+ohci_add_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head)
+{
+ DPRINTFN(8,("ohci_add_ed: sed=%p head=%p\n", sed, head));
+
+ SPLUSBCHECK;
+ sed->next = head->next;
+ sed->ed.ed_nexted = head->ed.ed_nexted;
+ head->next = sed;
+ head->ed.ed_nexted = htole32(sed->physaddr);
+}
+
+/*
+ * Remove an ED from the schedule. Called at splusb().
+ */
+void
+ohci_rem_ed(ohci_soft_ed_t *sed, ohci_soft_ed_t *head)
+{
+ ohci_soft_ed_t *p;
+
+ SPLUSBCHECK;
+
+ /* XXX */
+ for (p = head; p != NULL && p->next != sed; p = p->next)
+ ;
+ if (p == NULL)
+ panic("ohci_rem_ed: ED not found");
+ p->next = sed->next;
+ p->ed.ed_nexted = sed->ed.ed_nexted;
+}
+
+/*
+ * When a transfer is completed the TD is added to the done queue by
+ * the host controller. This queue is the processed by software.
+ * Unfortunately the queue contains the physical address of the TD
+ * and we have no simple way to translate this back to a kernel address.
+ * To make the translation possible (and fast) we use a hash table of
+ * TDs currently in the schedule. The physical address is used as the
+ * hash value.
+ */
+
+#define HASH(a) (((a) >> 4) % OHCI_HASH_SIZE)
+/* Called at splusb() */
+void
+ohci_hash_add_td(ohci_softc_t *sc, ohci_soft_td_t *std)
+{
+ int h = HASH(std->physaddr);
+
+ SPLUSBCHECK;
+
+ LIST_INSERT_HEAD(&sc->sc_hash_tds[h], std, hnext);
+}
+
+/* Called at splusb() */
+void
+ohci_hash_rem_td(ohci_softc_t *sc, ohci_soft_td_t *std)
+{
+ SPLUSBCHECK;
+
+ LIST_REMOVE(std, hnext);
+}
+
+ohci_soft_td_t *
+ohci_hash_find_td(ohci_softc_t *sc, ohci_physaddr_t a)
+{
+ int h = HASH(a);
+ ohci_soft_td_t *std;
+
+ /* if these are present they should be masked out at an earlier
+ * stage.
+ */
+ KASSERT((a&~OHCI_HEADMASK) == 0, ("%s: 0x%b has lower bits set\n",
+ device_get_nameunit(sc->sc_bus.bdev),
+ (int) a, "\20\1HALT\2TOGGLE"));
+
+ for (std = LIST_FIRST(&sc->sc_hash_tds[h]);
+ std != NULL;
+ std = LIST_NEXT(std, hnext))
+ if (std->physaddr == a)
+ return (std);
+
+ DPRINTF(("%s: ohci_hash_find_td: addr 0x%08lx not found\n",
+ device_get_nameunit(sc->sc_bus.bdev), (u_long) a));
+ return (NULL);
+}
+
+/* Called at splusb() */
+void
+ohci_hash_add_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+{
+ int h = HASH(sitd->physaddr);
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10,("ohci_hash_add_itd: sitd=%p physaddr=0x%08lx\n",
+ sitd, (u_long)sitd->physaddr));
+
+ LIST_INSERT_HEAD(&sc->sc_hash_itds[h], sitd, hnext);
+}
+
+/* Called at splusb() */
+void
+ohci_hash_rem_itd(ohci_softc_t *sc, ohci_soft_itd_t *sitd)
+{
+ SPLUSBCHECK;
+
+ DPRINTFN(10,("ohci_hash_rem_itd: sitd=%p physaddr=0x%08lx\n",
+ sitd, (u_long)sitd->physaddr));
+
+ LIST_REMOVE(sitd, hnext);
+}
+
+ohci_soft_itd_t *
+ohci_hash_find_itd(ohci_softc_t *sc, ohci_physaddr_t a)
+{
+ int h = HASH(a);
+ ohci_soft_itd_t *sitd;
+
+ for (sitd = LIST_FIRST(&sc->sc_hash_itds[h]);
+ sitd != NULL;
+ sitd = LIST_NEXT(sitd, hnext))
+ if (sitd->physaddr == a)
+ return (sitd);
+ return (NULL);
+}
+
+void
+ohci_timeout(void *addr)
+{
+ struct ohci_xfer *oxfer = addr;
+ struct ohci_pipe *opipe = (struct ohci_pipe *)oxfer->xfer.pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+
+ DPRINTF(("ohci_timeout: oxfer=%p\n", oxfer));
+
+ if (sc->sc_dying) {
+ ohci_abort_xfer(&oxfer->xfer, USBD_TIMEOUT);
+ return;
+ }
+
+ /* Execute the abort in a process context. */
+ usb_add_task(oxfer->xfer.pipe->device, &oxfer->abort_task,
+ USB_TASKQ_HC);
+}
+
+void
+ohci_timeout_task(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ int s;
+
+ DPRINTF(("ohci_timeout_task: xfer=%p\n", xfer));
+
+ s = splusb();
+ ohci_abort_xfer(xfer, USBD_TIMEOUT);
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+void
+ohci_dump_tds(ohci_soft_td_t *std)
+{
+ for (; std; std = std->nexttd)
+ ohci_dump_td(std);
+}
+
+void
+ohci_dump_td(ohci_soft_td_t *std)
+{
+ char sbuf[128];
+
+ bitmask_snprintf((u_int32_t)le32toh(std->td.td_flags),
+ "\20\23R\24OUT\25IN\31TOG1\32SETTOGGLE",
+ sbuf, sizeof(sbuf));
+
+ printf("TD(%p) at %08lx: %s delay=%d ec=%d cc=%d\ncbp=0x%08lx "
+ "nexttd=0x%08lx be=0x%08lx\n",
+ std, (u_long)std->physaddr, sbuf,
+ OHCI_TD_GET_DI(le32toh(std->td.td_flags)),
+ OHCI_TD_GET_EC(le32toh(std->td.td_flags)),
+ OHCI_TD_GET_CC(le32toh(std->td.td_flags)),
+ (u_long)le32toh(std->td.td_cbp),
+ (u_long)le32toh(std->td.td_nexttd),
+ (u_long)le32toh(std->td.td_be));
+}
+
+void
+ohci_dump_itd(ohci_soft_itd_t *sitd)
+{
+ int i;
+
+ printf("ITD(%p) at %08lx: sf=%d di=%d fc=%d cc=%d\n"
+ "bp0=0x%08lx next=0x%08lx be=0x%08lx\n",
+ sitd, (u_long)sitd->physaddr,
+ OHCI_ITD_GET_SF(le32toh(sitd->itd.itd_flags)),
+ OHCI_ITD_GET_DI(le32toh(sitd->itd.itd_flags)),
+ OHCI_ITD_GET_FC(le32toh(sitd->itd.itd_flags)),
+ OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags)),
+ (u_long)le32toh(sitd->itd.itd_bp0),
+ (u_long)le32toh(sitd->itd.itd_nextitd),
+ (u_long)le32toh(sitd->itd.itd_be));
+ for (i = 0; i < OHCI_ITD_NOFFSET; i++)
+ printf("offs[%d]=0x%04x ", i,
+ (u_int)le16toh(sitd->itd.itd_offset[i]));
+ printf("\n");
+}
+
+void
+ohci_dump_itds(ohci_soft_itd_t *sitd)
+{
+ for (; sitd; sitd = sitd->nextitd)
+ ohci_dump_itd(sitd);
+}
+
+void
+ohci_dump_ed(ohci_soft_ed_t *sed)
+{
+ char sbuf[128], sbuf2[128];
+
+ bitmask_snprintf((u_int32_t)le32toh(sed->ed.ed_flags),
+ "\20\14OUT\15IN\16LOWSPEED\17SKIP\20ISO",
+ sbuf, sizeof(sbuf));
+ bitmask_snprintf((u_int32_t)le32toh(sed->ed.ed_headp),
+ "\20\1HALT\2CARRY", sbuf2, sizeof(sbuf2));
+
+ printf("ED(%p) at 0x%08lx: addr=%d endpt=%d maxp=%d flags=%s\ntailp=0x%08lx "
+ "headflags=%s headp=0x%08lx nexted=0x%08lx\n",
+ sed, (u_long)sed->physaddr,
+ OHCI_ED_GET_FA(le32toh(sed->ed.ed_flags)),
+ OHCI_ED_GET_EN(le32toh(sed->ed.ed_flags)),
+ OHCI_ED_GET_MAXP(le32toh(sed->ed.ed_flags)), sbuf,
+ (u_long)le32toh(sed->ed.ed_tailp), sbuf2,
+ (u_long)le32toh(sed->ed.ed_headp),
+ (u_long)le32toh(sed->ed.ed_nexted));
+}
+#endif
+
+usbd_status
+ohci_open(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ u_int8_t addr = dev->address;
+ u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
+ ohci_soft_ed_t *sed;
+ ohci_soft_td_t *std;
+ ohci_soft_itd_t *sitd;
+ ohci_physaddr_t tdphys;
+ u_int32_t fmt;
+ usbd_status err;
+ int s;
+ int ival;
+
+ DPRINTFN(1, ("ohci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
+ pipe, addr, ed->bEndpointAddress, sc->sc_addr));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ std = NULL;
+ sed = NULL;
+
+ if (addr == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &ohci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | OHCI_INTR_ENDPT:
+ pipe->methods = &ohci_root_intr_methods;
+ break;
+ default:
+ return (USBD_INVAL);
+ }
+ } else {
+ sed = ohci_alloc_sed(sc);
+ if (sed == NULL)
+ goto bad0;
+ opipe->sed = sed;
+ if (xfertype == UE_ISOCHRONOUS) {
+ sitd = ohci_alloc_sitd(sc);
+ if (sitd == NULL)
+ goto bad1;
+ opipe->tail.itd = sitd;
+ opipe->aborting = 0;
+ tdphys = sitd->physaddr;
+ fmt = OHCI_ED_FORMAT_ISO;
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ fmt |= OHCI_ED_DIR_IN;
+ else
+ fmt |= OHCI_ED_DIR_OUT;
+ } else {
+ std = ohci_alloc_std(sc);
+ if (std == NULL)
+ goto bad1;
+ opipe->tail.td = std;
+ tdphys = std->physaddr;
+ fmt = OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD;
+ }
+ sed->ed.ed_flags = htole32(
+ OHCI_ED_SET_FA(addr) |
+ OHCI_ED_SET_EN(UE_GET_ADDR(ed->bEndpointAddress)) |
+ (dev->speed == USB_SPEED_LOW ? OHCI_ED_SPEED : 0) |
+ fmt |
+ OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize)));
+ sed->ed.ed_headp = htole32(tdphys |
+ (pipe->endpoint->savedtoggle ? OHCI_TOGGLECARRY : 0));
+ sed->ed.ed_tailp = htole32(tdphys);
+
+ switch (xfertype) {
+ case UE_CONTROL:
+ pipe->methods = &ohci_device_ctrl_methods;
+ err = usb_allocmem(&sc->sc_bus,
+ sizeof(usb_device_request_t),
+ 0, &opipe->u.ctl.reqdma);
+ if (err)
+ goto bad;
+ s = splusb();
+ ohci_add_ed(sed, sc->sc_ctrl_head);
+ splx(s);
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &ohci_device_intr_methods;
+ ival = pipe->interval;
+ if (ival == USBD_DEFAULT_INTERVAL)
+ ival = ed->bInterval;
+ return (ohci_device_setintr(sc, opipe, ival));
+ case UE_ISOCHRONOUS:
+ pipe->methods = &ohci_device_isoc_methods;
+ return (ohci_setup_isoc(pipe));
+ case UE_BULK:
+ pipe->methods = &ohci_device_bulk_methods;
+ s = splusb();
+ ohci_add_ed(sed, sc->sc_bulk_head);
+ splx(s);
+ break;
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ if (std != NULL)
+ ohci_free_std(sc, std);
+ bad1:
+ if (sed != NULL)
+ ohci_free_sed(sc, sed);
+ bad0:
+ return (USBD_NOMEM);
+
+}
+
+/*
+ * Close a reqular pipe.
+ * Assumes that there are no pending transactions.
+ */
+void
+ohci_close_pipe(usbd_pipe_handle pipe, ohci_soft_ed_t *head)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ int s;
+
+ s = splusb();
+#ifdef DIAGNOSTIC
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK)) {
+ ohci_soft_td_t *std;
+ std = ohci_hash_find_td(sc, le32toh(sed->ed.ed_headp));
+ printf("ohci_close_pipe: pipe not empty sed=%p hd=0x%x "
+ "tl=0x%x pipe=%p, std=%p\n", sed,
+ (int)le32toh(sed->ed.ed_headp),
+ (int)le32toh(sed->ed.ed_tailp),
+ pipe, std);
+#ifdef USB_DEBUG
+ usbd_dump_pipe(&opipe->pipe);
+#endif
+#ifdef USB_DEBUG
+ ohci_dump_ed(sed);
+ if (std)
+ ohci_dump_td(std);
+#endif
+ usb_delay_ms(&sc->sc_bus, 2);
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK))
+ printf("ohci_close_pipe: pipe still not empty\n");
+ }
+#endif
+ ohci_rem_ed(sed, head);
+ /* Make sure the host controller is not touching this ED */
+ usb_delay_ms(&sc->sc_bus, 1);
+ splx(s);
+ pipe->endpoint->savedtoggle =
+ (le32toh(sed->ed.ed_headp) & OHCI_TOGGLECARRY) ? 1 : 0;
+ ohci_free_sed(sc, opipe->sed);
+}
+
+/*
+ * Abort a device request.
+ * If this routine is called at splusb() it guarantees that the request
+ * will be removed from the hardware scheduling and that the callback
+ * for it will be called with USBD_CANCELLED status.
+ * It's impossible to guarantee that the requested transfer will not
+ * have happened since the hardware runs concurrently.
+ * If the transaction has already happened we rely on the ordinary
+ * interrupt processing to process it.
+ */
+void
+ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ struct ohci_xfer *oxfer = OXFER(xfer);
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ ohci_soft_td_t *p, *n;
+ ohci_physaddr_t headp;
+ int s, hit;
+
+ DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p sed=%p\n", xfer, opipe,sed));
+
+ if (sc->sc_dying) {
+ /* If we're dying, just do the software part. */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(xfer->pipe->device, &OXFER(xfer)->abort_task);
+ usb_transfer_complete(xfer);
+ splx(s);
+ return;
+ }
+
+ if (xfer->device->bus->intr_context || !curproc)
+ panic("ohci_abort_xfer: not in process context");
+
+ /*
+ * If an abort is already in progress then just wait for it to
+ * complete and return.
+ */
+ if (oxfer->ohci_xfer_flags & OHCI_XFER_ABORTING) {
+ DPRINTFN(2, ("ohci_abort_xfer: already aborting\n"));
+ /* No need to wait if we're aborting from a timeout. */
+ if (status == USBD_TIMEOUT)
+ return;
+ /* Override the status which might be USBD_TIMEOUT. */
+ xfer->status = status;
+ DPRINTFN(2, ("ohci_abort_xfer: waiting for abort to finish\n"));
+ oxfer->ohci_xfer_flags |= OHCI_XFER_ABORTWAIT;
+ while (oxfer->ohci_xfer_flags & OHCI_XFER_ABORTING)
+ tsleep(&oxfer->ohci_xfer_flags, PZERO, "ohciaw", 0);
+ return;
+ }
+
+ /*
+ * Step 1: Make interrupt routine and hardware ignore xfer.
+ */
+ s = splusb();
+ oxfer->ohci_xfer_flags |= OHCI_XFER_ABORTING;
+ xfer->status = status; /* make software ignore it */
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(xfer->pipe->device, &OXFER(xfer)->abort_task);
+ splx(s);
+ DPRINTFN(1,("ohci_abort_xfer: stop ed=%p\n", sed));
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
+
+ /*
+ * Step 2: Wait until we know hardware has finished any possible
+ * use of the xfer. Also make sure the soft interrupt routine
+ * has run.
+ */
+ usb_delay_ms(opipe->pipe.device->bus, 20); /* Hardware finishes in 1ms */
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ tsleep(&sc->sc_softwake, PZERO, "ohciab", 0);
+#endif /* USB_USE_SOFTINTR */
+ splx(s);
+
+ /*
+ * Step 3: Remove any vestiges of the xfer from the hardware.
+ * The complication here is that the hardware may have executed
+ * beyond the xfer we're trying to abort. So as we're scanning
+ * the TDs of this xfer we check if the hardware points to
+ * any of them.
+ */
+ s = splusb(); /* XXX why? */
+ p = xfer->hcpriv;
+#ifdef DIAGNOSTIC
+ if (p == NULL) {
+ oxfer->ohci_xfer_flags &= ~OHCI_XFER_ABORTING; /* XXX */
+ splx(s);
+ printf("ohci_abort_xfer: hcpriv is NULL\n");
+ return;
+ }
+#endif
+#ifdef USB_DEBUG
+ if (ohcidebug > 1) {
+ DPRINTF(("ohci_abort_xfer: sed=\n"));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(p);
+ }
+#endif
+ headp = le32toh(sed->ed.ed_headp) & OHCI_HEADMASK;
+ hit = 0;
+ for (; p->xfer == xfer; p = n) {
+ hit |= headp == p->physaddr;
+ n = p->nexttd;
+ ohci_free_std(sc, p);
+ }
+ /* Zap headp register if hardware pointed inside the xfer. */
+ if (hit) {
+ DPRINTFN(1,("ohci_abort_xfer: set hd=0x08%x, tl=0x%08x\n",
+ (int)p->physaddr, (int)le32toh(sed->ed.ed_tailp)));
+ sed->ed.ed_headp = htole32(p->physaddr); /* unlink TDs */
+ } else {
+ DPRINTFN(1,("ohci_abort_xfer: no hit\n"));
+ }
+
+ /*
+ * Step 4: Turn on hardware again.
+ */
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */
+
+ /*
+ * Step 5: Execute callback.
+ */
+ /* Do the wakeup first to avoid touching the xfer after the callback. */
+ oxfer->ohci_xfer_flags &= ~OHCI_XFER_ABORTING;
+ if (oxfer->ohci_xfer_flags & OHCI_XFER_ABORTWAIT) {
+ oxfer->ohci_xfer_flags &= ~OHCI_XFER_ABORTWAIT;
+ wakeup(&oxfer->ohci_xfer_flags);
+ }
+ usb_transfer_complete(xfer);
+
+ splx(s);
+}
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+static usb_device_descriptor_t ohci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x01}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+static usb_config_descriptor_t ohci_confd = {
+ USB_CONFIG_DESCRIPTOR_SIZE,
+ UDESC_CONFIG,
+ {USB_CONFIG_DESCRIPTOR_SIZE +
+ USB_INTERFACE_DESCRIPTOR_SIZE +
+ USB_ENDPOINT_DESCRIPTOR_SIZE},
+ 1,
+ 1,
+ 0,
+ UC_SELF_POWERED,
+ 0 /* max power */
+};
+
+static usb_interface_descriptor_t ohci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_FSHUB,
+ 0
+};
+
+static usb_endpoint_descriptor_t ohci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | OHCI_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8, 0}, /* max packet */
+ 255
+};
+
+static usb_hub_descriptor_t ohci_hubd = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 0,
+ {0,0},
+ 0,
+ 0,
+ {0},
+};
+
+static int
+ohci_str(usb_string_descriptor_t *p, int l, const char *s)
+{
+ int i;
+
+ if (l == 0)
+ return (0);
+ p->bLength = 2 * strlen(s) + 2;
+ if (l == 1)
+ return (1);
+ p->bDescriptorType = UDESC_STRING;
+ l -= 2;
+ for (i = 0; s[i] && l > 1; i++, l -= 2)
+ USETW2(p->bString[i], 0, s[i]);
+ return (2*i+2);
+}
+
+/*
+ * Simulate a hardware hub by handling all the necessary requests.
+ */
+static usbd_status
+ohci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_root_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ohci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)xfer->pipe->device->bus;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ int port, i;
+ int s, len, value, index, l, totlen = 0;
+ usb_port_status_t ps;
+ usb_hub_descriptor_t hubd;
+ usbd_status err;
+ u_int32_t v;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ /* XXX panic */
+ return (USBD_INVAL);
+#endif
+ req = &xfer->request;
+
+ DPRINTFN(4,("ohci_root_ctrl_control type=0x%02x request=%02x\n",
+ req->bmRequestType, req->bRequest));
+
+ len = UGETW(req->wLength);
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ if (len != 0)
+ buf = xfer->buffer;
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch(C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ if (len > 0) {
+ *(u_int8_t *)buf = sc->sc_conf;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ DPRINTFN(8,("ohci_root_ctrl_control wValue=0x%04x\n", value));
+ switch(value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ USETW(ohci_devd.idVendor, sc->sc_id_vendor);
+ memcpy(buf, &ohci_devd, l);
+ break;
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &ohci_confd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ohci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &ohci_endpd, l);
+ break;
+ case UDESC_STRING:
+ if (len == 0)
+ break;
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ switch (value & 0xff) {
+ case 1: /* Vendor */
+ totlen = ohci_str(buf, len, sc->sc_vendor);
+ break;
+ case 2: /* Product */
+ totlen = ohci_str(buf, len, "OHCI root hub");
+ break;
+ }
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ if (len > 0) {
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED);
+ totlen = 2;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, 0);
+ totlen = 2;
+ }
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(8, ("ohci_root_ctrl_control: UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = OHCI_RH_PORT_STATUS(index);
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_CURRENT_CONNECT_STATUS);
+ break;
+ case UHF_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_OVERCURRENT_INDICATOR);
+ break;
+ case UHF_PORT_POWER:
+ /* Yes, writing to the LOW_SPEED bit clears power. */
+ OWRITE4(sc, port, UPS_LOW_SPEED);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ OWRITE4(sc, port, UPS_C_CONNECT_STATUS << 16);
+ break;
+ case UHF_C_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_C_PORT_ENABLED << 16);
+ break;
+ case UHF_C_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_C_SUSPEND << 16);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ OWRITE4(sc, port, UPS_C_OVERCURRENT_INDICATOR << 16);
+ break;
+ case UHF_C_PORT_RESET:
+ OWRITE4(sc, port, UPS_C_PORT_RESET << 16);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* Enable RHSC interrupt if condition is cleared. */
+ if ((OREAD4(sc, port) >> 16) == 0)
+ ohci_rhsc_able(sc, 1);
+ break;
+ default:
+ break;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A);
+ hubd = ohci_hubd;
+ hubd.bNbrPorts = sc->sc_noport;
+ USETW(hubd.wHubCharacteristics,
+ (v & OHCI_NPS ? UHD_PWR_NO_SWITCH :
+ v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL)
+ /* XXX overcurrent */
+ );
+ hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v);
+ v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B);
+ for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8)
+ hubd.DeviceRemovable[i++] = (u_int8_t)v;
+ hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
+ l = min(len, hubd.bDescLength);
+ totlen = l;
+ memcpy(buf, &hubd, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len); /* ? XXX */
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(8,("ohci_root_ctrl_transfer: get port status i=%d\n",
+ index));
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = OREAD4(sc, OHCI_RH_PORT_STATUS(index));
+ DPRINTFN(8,("ohci_root_ctrl_transfer: port status=0x%04x\n",
+ v));
+ USETW(ps.wPortStatus, v);
+ USETW(ps.wPortChange, v >> 16);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if (index < 1 || index > sc->sc_noport) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = OHCI_RH_PORT_STATUS(index);
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_PORT_ENABLED);
+ break;
+ case UHF_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_SUSPEND);
+ break;
+ case UHF_PORT_RESET:
+ DPRINTFN(5,("ohci_root_ctrl_transfer: reset port %d\n",
+ index));
+ OWRITE4(sc, port, UPS_RESET);
+ for (i = 0; i < 5; i++) {
+ usb_delay_ms(&sc->sc_bus,
+ USB_PORT_ROOT_RESET_DELAY);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if ((OREAD4(sc, port) & UPS_RESET) == 0)
+ break;
+ }
+ DPRINTFN(8,("ohci port %d reset, status = 0x%04x\n",
+ index, OREAD4(sc, port)));
+ break;
+ case UHF_PORT_POWER:
+ DPRINTFN(2,("ohci_root_ctrl_transfer: set port power "
+ "%d\n", index));
+ OWRITE4(sc, port, UPS_PORT_POWER);
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ ret:
+ xfer->status = err;
+ s = splusb();
+ hacksync(xfer); /* XXX to compensate for usb_transfer_complete */
+ usb_transfer_complete(xfer);
+ splx(s);
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root control request. */
+static void
+ohci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+/* Close the root pipe. */
+static void
+ohci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("ohci_root_ctrl_close\n"));
+ /* Nothing to do. */
+}
+
+static usbd_status
+ohci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_root_intr_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ohci_root_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ sc->sc_intrxfer = xfer;
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root interrupt request. */
+static void
+ohci_root_intr_abort(usbd_xfer_handle xfer)
+{
+ int s;
+
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("ohci_root_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ xfer->status = USBD_CANCELLED;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+}
+
+/* Close the root pipe. */
+static void
+ohci_root_intr_close(usbd_pipe_handle pipe)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ohci_root_intr_close\n"));
+
+ sc->sc_intrxfer = NULL;
+}
+
+/************************/
+
+static usbd_status
+ohci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_device_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ohci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ ohci_softc_t *sc = (ohci_softc_t *)xfer->pipe->device->bus;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST)) {
+ /* XXX panic */
+ printf("ohci_device_ctrl_transfer: not a request\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ err = ohci_device_request(xfer);
+ if (err)
+ return (err);
+
+ if (sc->sc_bus.use_polling)
+ ohci_waitintr(sc, xfer);
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a device control request. */
+static void
+ohci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ohci_device_ctrl_abort: xfer=%p\n", xfer));
+ ohci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+static void
+ohci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ohci_device_ctrl_close: pipe=%p\n", pipe));
+ ohci_close_pipe(pipe, sc->sc_ctrl_head);
+ ohci_free_std(sc, opipe->tail.td);
+}
+
+/************************/
+
+static void
+ohci_device_clear_toggle(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+
+ opipe->sed->ed.ed_headp &= htole32(~OHCI_TOGGLECARRY);
+}
+
+static void
+ohci_noop(usbd_pipe_handle pipe)
+{
+}
+
+static usbd_status
+ohci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_device_bulk_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ohci_device_bulk_start(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ int addr = dev->address;
+ ohci_soft_td_t *data, *tail, *tdp;
+ ohci_soft_ed_t *sed;
+ int s, len, isread, endpt;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST) {
+ /* XXX panic */
+ printf("ohci_device_bulk_start: a request\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ len = xfer->length;
+ endpt = xfer->pipe->endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sed = opipe->sed;
+
+ DPRINTFN(4,("ohci_device_bulk_start: xfer=%p len=%d isread=%d "
+ "flags=%d endpt=%d\n", xfer, len, isread, xfer->flags,
+ endpt));
+
+ opipe->u.bulk.isread = isread;
+ opipe->u.bulk.length = len;
+
+ /* Update device address */
+ sed->ed.ed_flags = htole32(
+ (le32toh(sed->ed.ed_flags) & ~OHCI_ED_ADDRMASK) |
+ OHCI_ED_SET_FA(addr));
+
+ /* Allocate a chain of new TDs (including a new tail). */
+ data = opipe->tail.td;
+ err = ohci_alloc_std_chain(opipe, sc, len, isread, xfer,
+ data, &tail);
+ /* We want interrupt at the end of the transfer. */
+ tail->td.td_flags &= htole32(~OHCI_TD_INTR_MASK);
+ tail->td.td_flags |= htole32(OHCI_TD_SET_DI(1));
+ tail->flags |= OHCI_CALL_DONE;
+ tail = tail->nexttd; /* point at sentinel */
+ if (err)
+ return (err);
+
+ tail->xfer = NULL;
+ xfer->hcpriv = data;
+
+ DPRINTFN(4,("ohci_device_bulk_start: ed_flags=0x%08x td_flags=0x%08x "
+ "td_cbp=0x%08x td_be=0x%08x\n",
+ (int)le32toh(sed->ed.ed_flags),
+ (int)le32toh(data->td.td_flags),
+ (int)le32toh(data->td.td_cbp),
+ (int)le32toh(data->td.td_be)));
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ ohci_dump_ed(sed);
+ ohci_dump_tds(data);
+ }
+#endif
+
+ /* Insert ED in schedule */
+ s = splusb();
+ for (tdp = data; tdp != tail; tdp = tdp->nexttd) {
+ tdp->xfer = xfer;
+ }
+ sed->ed.ed_tailp = htole32(tail->physaddr);
+ opipe->tail.td = tail;
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ ohci_timeout, xfer);
+ }
+
+#if 0
+/* This goes wrong if we are too slow. */
+ if (ohcidebug > 10) {
+ delay(10000);
+ DPRINTF(("ohci_device_intr_transfer: status=%x\n",
+ OREAD4(sc, OHCI_COMMAND_STATUS)));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(data);
+ }
+#endif
+
+ splx(s);
+
+ if (sc->sc_bus.use_polling)
+ ohci_waitintr(sc, xfer);
+
+ return (USBD_IN_PROGRESS);
+}
+
+static void
+ohci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("ohci_device_bulk_abort: xfer=%p\n", xfer));
+ ohci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/*
+ * Close a device bulk pipe.
+ */
+static void
+ohci_device_bulk_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+
+ DPRINTF(("ohci_device_bulk_close: pipe=%p\n", pipe));
+ ohci_close_pipe(pipe, sc->sc_bulk_head);
+ ohci_free_std(sc, opipe->tail.td);
+}
+
+/************************/
+
+static usbd_status
+ohci_device_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /* Pipe isn't running, start first */
+ return (ohci_device_intr_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+static usbd_status
+ohci_device_intr_start(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ DPRINTFN(3, ("ohci_device_intr_start: xfer=%p len=%d "
+ "flags=%d priv=%p\n",
+ xfer, xfer->length, xfer->flags, xfer->priv));
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("ohci_device_intr_start: a request");
+#endif
+
+ err = ohci_device_intr_insert(sc, xfer);
+ if (err)
+ return (err);
+
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP);
+
+ return (USBD_IN_PROGRESS);
+}
+
+/*
+ * Insert an interrupt transfer into an endpoint descriptor list
+ */
+static usbd_status
+ohci_device_intr_insert(ohci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_soft_ed_t *sed = opipe->sed;
+ ohci_soft_td_t *data, *tail;
+ ohci_physaddr_t dataphys, physend;
+ int s;
+
+ DPRINTFN(4, ("ohci_device_intr_insert: xfer=%p", xfer));
+
+ data = opipe->tail.td;
+ tail = ohci_alloc_std(sc);
+ if (tail == NULL)
+ return (USBD_NOMEM);
+ tail->xfer = NULL;
+
+ data->td.td_flags = htole32(
+ OHCI_TD_IN | OHCI_TD_NOCC |
+ OHCI_TD_SET_DI(1) | OHCI_TD_TOGGLE_CARRY);
+ if (xfer->flags & USBD_SHORT_XFER_OK)
+ data->td.td_flags |= htole32(OHCI_TD_R);
+ /*
+ * Assume a short mapping with no complications, which
+ * should always be true for <= 4k buffers in contiguous
+ * virtual memory. The data can take the following forms:
+ * 1 segment in 1 OHCI page
+ * 1 segment in 2 OHCI pages
+ * 2 segments in 2 OHCI pages
+ * (see comment in ohci_alloc_std_chain() for details)
+ */
+ KASSERT(xfer->length > 0 && xfer->length <= OHCI_PAGE_SIZE,
+ ("ohci_device_intr_insert: bad length %d", xfer->length));
+ dataphys = xfer->dmamap.segs[0].ds_addr;
+ physend = dataphys + xfer->length - 1;
+ if (xfer->dmamap.nsegs == 2) {
+ KASSERT(OHCI_PAGE_OFFSET(dataphys +
+ xfer->dmamap.segs[0].ds_len) == 0,
+ ("ohci_device_intr_insert: bad seg 0 termination"));
+ physend = xfer->dmamap.segs[1].ds_addr + xfer->length -
+ xfer->dmamap.segs[0].ds_len - 1;
+ } else {
+ KASSERT(xfer->dmamap.nsegs == 1,
+ ("ohci_device_intr_insert: bad seg count %d",
+ (u_int)xfer->dmamap.nsegs));
+ }
+ data->td.td_cbp = htole32(dataphys);
+ data->nexttd = tail;
+ data->td.td_nexttd = htole32(tail->physaddr);
+ data->td.td_be = htole32(physend);
+ data->len = xfer->length;
+ data->xfer = xfer;
+ data->flags = OHCI_CALL_DONE | OHCI_ADD_LEN;
+ xfer->hcpriv = data;
+ xfer->actlen = 0;
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ DPRINTF(("ohci_device_intr_insert:\n"));
+ ohci_dump_ed(sed);
+ ohci_dump_tds(data);
+ }
+#endif
+
+ /* Insert ED in schedule */
+ s = splusb();
+ sed->ed.ed_tailp = htole32(tail->physaddr);
+ opipe->tail.td = tail;
+ splx(s);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Abort a device control request. */
+static void
+ohci_device_intr_abort(usbd_xfer_handle xfer)
+{
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("ohci_device_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ ohci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device interrupt pipe. */
+static void
+ohci_device_intr_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ int nslots = opipe->u.intr.nslots;
+ int pos = opipe->u.intr.pos;
+ int j;
+ ohci_soft_ed_t *p, *sed = opipe->sed;
+ int s;
+
+ DPRINTFN(1,("ohci_device_intr_close: pipe=%p nslots=%d pos=%d\n",
+ pipe, nslots, pos));
+ s = splusb();
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP);
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK))
+ usb_delay_ms(&sc->sc_bus, 2);
+#ifdef DIAGNOSTIC
+ if ((le32toh(sed->ed.ed_tailp) & OHCI_HEADMASK) !=
+ (le32toh(sed->ed.ed_headp) & OHCI_HEADMASK))
+ panic("%s: Intr pipe %p still has TDs queued",
+ device_get_nameunit(sc->sc_bus.bdev), pipe);
+#endif
+
+ for (p = sc->sc_eds[pos]; p && p->next != sed; p = p->next)
+ ;
+#ifdef DIAGNOSTIC
+ if (p == NULL)
+ panic("ohci_device_intr_close: ED not found");
+#endif
+ p->next = sed->next;
+ p->ed.ed_nexted = sed->ed.ed_nexted;
+ splx(s);
+
+ for (j = 0; j < nslots; j++)
+ --sc->sc_bws[(pos * nslots + j) % OHCI_NO_INTRS];
+
+ ohci_free_std(sc, opipe->tail.td);
+ ohci_free_sed(sc, opipe->sed);
+}
+
+static usbd_status
+ohci_device_setintr(ohci_softc_t *sc, struct ohci_pipe *opipe, int ival)
+{
+ int i, j, s, best;
+ u_int npoll, slow, shigh, nslots;
+ u_int bestbw, bw;
+ ohci_soft_ed_t *hsed, *sed = opipe->sed;
+
+ DPRINTFN(2, ("ohci_setintr: pipe=%p\n", opipe));
+ if (ival == 0) {
+ printf("ohci_setintr: 0 interval\n");
+ return (USBD_INVAL);
+ }
+
+ npoll = OHCI_NO_INTRS;
+ while (npoll > ival)
+ npoll /= 2;
+ DPRINTFN(2, ("ohci_setintr: ival=%d npoll=%d\n", ival, npoll));
+
+ /*
+ * We now know which level in the tree the ED must go into.
+ * Figure out which slot has most bandwidth left over.
+ * Slots to examine:
+ * npoll
+ * 1 0
+ * 2 1 2
+ * 4 3 4 5 6
+ * 8 7 8 9 10 11 12 13 14
+ * N (N-1) .. (N-1+N-1)
+ */
+ slow = npoll-1;
+ shigh = slow + npoll;
+ nslots = OHCI_NO_INTRS / npoll;
+ for (best = i = slow, bestbw = ~0; i < shigh; i++) {
+ bw = 0;
+ for (j = 0; j < nslots; j++)
+ bw += sc->sc_bws[(i * nslots + j) % OHCI_NO_INTRS];
+ if (bw < bestbw) {
+ best = i;
+ bestbw = bw;
+ }
+ }
+ DPRINTFN(2, ("ohci_setintr: best=%d(%d..%d) bestbw=%d\n",
+ best, slow, shigh, bestbw));
+
+ s = splusb();
+ hsed = sc->sc_eds[best];
+ sed->next = hsed->next;
+ sed->ed.ed_nexted = hsed->ed.ed_nexted;
+ hsed->next = sed;
+ hsed->ed.ed_nexted = htole32(sed->physaddr);
+ splx(s);
+
+ for (j = 0; j < nslots; j++)
+ ++sc->sc_bws[(best * nslots + j) % OHCI_NO_INTRS];
+ opipe->u.intr.nslots = nslots;
+ opipe->u.intr.pos = best;
+
+ DPRINTFN(5, ("ohci_setintr: returns %p\n", opipe));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/***********************/
+
+usbd_status
+ohci_device_isoc_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ DPRINTFN(5,("ohci_device_isoc_transfer: xfer=%p\n", xfer));
+
+ /* Put it on our queue, */
+ err = usb_insert_transfer(xfer);
+
+ /* bail out on error, */
+ if (err && err != USBD_IN_PROGRESS)
+ return (err);
+
+ /* XXX should check inuse here */
+
+ /* insert into schedule, */
+ ohci_device_isoc_enter(xfer);
+
+ /* and start if the pipe wasn't running */
+ if (!err)
+ ohci_device_isoc_start(STAILQ_FIRST(&xfer->pipe->queue));
+
+ return (err);
+}
+
+void
+ohci_device_isoc_enter(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ usbd_device_handle dev = opipe->pipe.device;
+ ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
+ ohci_soft_ed_t *sed = opipe->sed;
+ struct iso *iso = &opipe->u.iso;
+ struct usb_dma_mapping *dma = &xfer->dmamap;
+ ohci_soft_itd_t *sitd, *nsitd;
+ ohci_physaddr_t dataphys, bp0, physend, prevpage;
+ int curlen, i, len, ncur, nframes, npages, seg, segoff;
+ int s;
+
+ DPRINTFN(1,("ohci_device_isoc_enter: used=%d next=%d xfer=%p "
+ "nframes=%d\n",
+ iso->inuse, iso->next, xfer, xfer->nframes));
+
+ if (sc->sc_dying)
+ return;
+
+ if (iso->next == -1) {
+ /* Not in use yet, schedule it a few frames ahead. */
+ iso->next = le32toh(sc->sc_hcca->hcca_frame_number) + 5;
+ DPRINTFN(2,("ohci_device_isoc_enter: start next=%d\n",
+ iso->next));
+ }
+
+ sitd = opipe->tail.itd;
+ nframes = xfer->nframes;
+ xfer->hcpriv = sitd;
+ seg = 0;
+ segoff = 0;
+ i = 0;
+ while (i < nframes) {
+ /*
+ * Fill in as many ITD frames as possible.
+ */
+ KASSERT(seg < dma->nsegs, ("ohci_device_isoc_enter: overrun"));
+ bp0 = dma->segs[seg].ds_addr + segoff;
+ sitd->itd.itd_bp0 = htole32(bp0);
+ prevpage = OHCI_PAGE(bp0);
+ npages = 1;
+
+ ncur = 0;
+ while (ncur < OHCI_ITD_NOFFSET && i < nframes) {
+ /* Find the frame start and end physical addresses. */
+ len = xfer->frlengths[i];
+ dataphys = dma->segs[seg].ds_addr + segoff;
+ curlen = dma->segs[seg].ds_len - segoff;
+ if (len > curlen) {
+ KASSERT(seg + 1 < dma->nsegs,
+ ("ohci_device_isoc_enter: overrun2"));
+ seg++;
+ segoff = len - curlen;
+ } else {
+ segoff += len;
+ }
+ KASSERT(segoff <= dma->segs[seg].ds_len,
+ ("ohci_device_isoc_enter: overrun3"));
+ physend = dma->segs[seg].ds_addr + segoff - 1;
+
+ /* Check if there would be more than 2 pages . */
+ if (OHCI_PAGE(dataphys) != prevpage) {
+ prevpage = OHCI_PAGE(dataphys);
+ npages++;
+ }
+ if (OHCI_PAGE(physend) != prevpage) {
+ prevpage = OHCI_PAGE(physend);
+ npages++;
+ }
+ if (npages > 2) {
+ /* We cannot fit this frame now. */
+ segoff -= len;
+ if (segoff < 0) {
+ seg--;
+ segoff += dma->segs[seg].ds_len;
+ }
+ break;
+ }
+
+ sitd->itd.itd_be = htole32(physend);
+ sitd->itd.itd_offset[ncur] =
+ htole16(OHCI_ITD_MK_OFFS(OHCI_PAGE(dataphys) ==
+ OHCI_PAGE(bp0) ? 0 : 1, dataphys));
+ i++;
+ ncur++;
+ }
+ if (segoff >= dma->segs[seg].ds_len) {
+ KASSERT(segoff == dma->segs[seg].ds_len,
+ ("ohci_device_isoc_enter: overlap"));
+ seg++;
+ segoff = 0;
+ }
+
+ /* Allocate next ITD */
+ nsitd = ohci_alloc_sitd(sc);
+ if (nsitd == NULL) {
+ /* XXX what now? */
+ printf("%s: isoc TD alloc failed\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ return;
+ }
+
+ /* Fill out remaining fields of current ITD */
+ sitd->nextitd = nsitd;
+ sitd->itd.itd_nextitd = htole32(nsitd->physaddr);
+ sitd->xfer = xfer;
+ if (i < nframes) {
+ sitd->itd.itd_flags = htole32(
+ OHCI_ITD_NOCC |
+ OHCI_ITD_SET_SF(iso->next) |
+ OHCI_ITD_SET_DI(6) | /* delay intr a little */
+ OHCI_ITD_SET_FC(ncur));
+ sitd->flags = OHCI_ITD_ACTIVE;
+ } else {
+ sitd->itd.itd_flags = htole32(
+ OHCI_ITD_NOCC |
+ OHCI_ITD_SET_SF(iso->next) |
+ OHCI_ITD_SET_DI(0) |
+ OHCI_ITD_SET_FC(ncur));
+ sitd->flags = OHCI_CALL_DONE | OHCI_ITD_ACTIVE;
+ }
+ iso->next += ncur;
+
+ sitd = nsitd;
+ }
+
+ iso->inuse += nframes;
+
+ /* XXX pretend we did it all */
+ xfer->actlen = 0;
+ for (i = 0; i < nframes; i++)
+ xfer->actlen += xfer->frlengths[i];
+
+ xfer->status = USBD_IN_PROGRESS;
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ DPRINTF(("ohci_device_isoc_enter: frame=%d\n",
+ le32toh(sc->sc_hcca->hcca_frame_number)));
+ ohci_dump_itds(xfer->hcpriv);
+ ohci_dump_ed(sed);
+ }
+#endif
+
+ s = splusb();
+ opipe->tail.itd = sitd;
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP);
+ sed->ed.ed_tailp = htole32(sitd->physaddr);
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (ohcidebug > 5) {
+ delay(150000);
+ DPRINTF(("ohci_device_isoc_enter: after frame=%d\n",
+ le32toh(sc->sc_hcca->hcca_frame_number)));
+ ohci_dump_itds(xfer->hcpriv);
+ ohci_dump_ed(sed);
+ }
+#endif
+}
+
+usbd_status
+ohci_device_isoc_start(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed;
+ int s;
+
+ DPRINTFN(5,("ohci_device_isoc_start: xfer=%p\n", xfer));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->status != USBD_IN_PROGRESS)
+ printf("ohci_device_isoc_start: not in progress %p\n", xfer);
+#endif
+
+ /* XXX anything to do? */
+
+ s = splusb();
+ sed = opipe->sed; /* Turn off ED skip-bit to start processing */
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* ED's ITD list.*/
+ splx(s);
+
+ return (USBD_IN_PROGRESS);
+}
+
+void
+ohci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
+ ohci_soft_ed_t *sed;
+ ohci_soft_itd_t *sitd, *sitdnext, *tmp_sitd;
+ int s,undone,num_sitds;
+
+ s = splusb();
+ opipe->aborting = 1;
+
+ DPRINTFN(1,("ohci_device_isoc_abort: xfer=%p\n", xfer));
+
+ /* Transfer is already done. */
+ if (xfer->status != USBD_NOT_STARTED &&
+ xfer->status != USBD_IN_PROGRESS) {
+ splx(s);
+ printf("ohci_device_isoc_abort: early return\n");
+ return;
+ }
+
+ /* Give xfer the requested abort code. */
+ xfer->status = USBD_CANCELLED;
+
+ sed = opipe->sed;
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* force hardware skip */
+
+ num_sitds = 0;
+ sitd = xfer->hcpriv;
+#ifdef DIAGNOSTIC
+ if (sitd == NULL) {
+ splx(s);
+ printf("ohci_device_isoc_abort: hcpriv==0\n");
+ return;
+ }
+#endif
+ for (; sitd != NULL && sitd->xfer == xfer; sitd = sitd->nextitd) {
+ num_sitds++;
+#ifdef DIAGNOSTIC
+ DPRINTFN(1,("abort sets done sitd=%p\n", sitd));
+ sitd->isdone = 1;
+#endif
+ }
+
+ splx(s);
+
+ /*
+ * Each sitd has up to OHCI_ITD_NOFFSET transfers, each can
+ * take a usb 1ms cycle. Conservatively wait for it to drain.
+ * Even with DMA done, it can take awhile for the "batch"
+ * delivery of completion interrupts to occur thru the controller.
+ */
+
+ do {
+ usb_delay_ms(&sc->sc_bus, 2*(num_sitds*OHCI_ITD_NOFFSET));
+
+ undone = 0;
+ tmp_sitd = xfer->hcpriv;
+ for (; tmp_sitd != NULL && tmp_sitd->xfer == xfer;
+ tmp_sitd = tmp_sitd->nextitd) {
+ if (OHCI_CC_NO_ERROR ==
+ OHCI_ITD_GET_CC(le32toh(tmp_sitd->itd.itd_flags)) &&
+ tmp_sitd->flags & OHCI_ITD_ACTIVE &&
+ (tmp_sitd->flags & OHCI_ITD_INTFIN) == 0)
+ undone++;
+ }
+ } while( undone != 0 );
+
+ /* Free the sitds */
+ for (sitd = xfer->hcpriv; sitd->xfer == xfer;
+ sitd = sitdnext) {
+ sitdnext = sitd->nextitd;
+ ohci_free_sitd(sc, sitd);
+ }
+
+ s = splusb();
+
+ /* Run callback. */
+ usb_transfer_complete(xfer);
+
+ /* There is always a `next' sitd so link it up. */
+ sed->ed.ed_headp = htole32(sitd->physaddr);
+
+ sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* remove hardware skip */
+
+ splx(s);
+}
+
+void
+ohci_device_isoc_done(usbd_xfer_handle xfer)
+{
+ /* This null routine corresponds to non-isoc "done()" routines
+ * that free the stds associated with an xfer after a completed
+ * xfer interrupt. However, in the case of isoc transfers, the
+ * sitds associated with the transfer have already been processed
+ * and reallocated for the next iteration by
+ * "ohci_device_isoc_transfer()".
+ *
+ * Routine "usb_transfer_complete()" is called at the end of every
+ * relevant usb interrupt. "usb_transfer_complete()" indirectly
+ * calls 1) "ohci_device_isoc_transfer()" (which keeps pumping the
+ * pipeline by setting up the next transfer iteration) and 2) then
+ * calls "ohci_device_isoc_done()". Isoc transfers have not been
+ * working for the ohci usb because this routine was trashing the
+ * xfer set up for the next iteration (thus, only the first
+ * UGEN_NISOREQS xfers outstanding on an open would work). Perhaps
+ * this could all be re-factored, but that's another pass...
+ */
+}
+
+usbd_status
+ohci_setup_isoc(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ struct iso *iso = &opipe->u.iso;
+ int s;
+
+ iso->next = -1;
+ iso->inuse = 0;
+
+ s = splusb();
+ ohci_add_ed(opipe->sed, sc->sc_isoc_head);
+ splx(s);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+ohci_device_isoc_close(usbd_pipe_handle pipe)
+{
+ struct ohci_pipe *opipe = (struct ohci_pipe *)pipe;
+ ohci_softc_t *sc = (ohci_softc_t *)pipe->device->bus;
+ ohci_soft_ed_t *sed;
+
+ DPRINTF(("ohci_device_isoc_close: pipe=%p\n", pipe));
+
+ sed = opipe->sed;
+ sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop device. */
+
+ ohci_close_pipe(pipe, sc->sc_isoc_head); /* Stop isoc list, free ED.*/
+
+ /* up to NISOREQs xfers still outstanding. */
+
+#ifdef DIAGNOSTIC
+ opipe->tail.itd->isdone = 1;
+#endif
+ ohci_free_sitd(sc, opipe->tail.itd); /* Next `avail free' sitd.*/
+}
diff --git a/sys/legacy/dev/usb/ohci_pci.c b/sys/legacy/dev/usb/ohci_pci.c
new file mode 100644
index 0000000..fadffb6
--- /dev/null
+++ b/sys/legacy/dev/usb/ohci_pci.c
@@ -0,0 +1,411 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Open Host Controller driver.
+ *
+ * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf
+ */
+
+/* The low level controller code for OHCI has been split into
+ * PCI probes and OHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/ohcireg.h>
+#include <dev/usb/ohcivar.h>
+
+#define PCI_OHCI_VENDORID_ACERLABS 0x10b9
+#define PCI_OHCI_VENDORID_AMD 0x1022
+#define PCI_OHCI_VENDORID_APPLE 0x106b
+#define PCI_OHCI_VENDORID_ATI 0x1002
+#define PCI_OHCI_VENDORID_CMDTECH 0x1095
+#define PCI_OHCI_VENDORID_NEC 0x1033
+#define PCI_OHCI_VENDORID_NVIDIA 0x12D2
+#define PCI_OHCI_VENDORID_NVIDIA2 0x10DE
+#define PCI_OHCI_VENDORID_OPTI 0x1045
+#define PCI_OHCI_VENDORID_SIS 0x1039
+#define PCI_OHCI_VENDORID_SUN 0x108e
+
+#define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9
+static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB controller";
+
+#define PCI_OHCI_DEVICEID_AMD756 0x740c1022
+static const char *ohci_device_amd756 = "AMD-756 USB Controller";
+
+#define PCI_OHCI_DEVICEID_AMD766 0x74141022
+static const char *ohci_device_amd766 = "AMD-766 USB Controller";
+
+#define PCI_OHCI_DEVICEID_SB400_1 0x43741002
+#define PCI_OHCI_DEVICEID_SB400_2 0x43751002
+static const char *ohci_device_sb400 = "ATI SB400 USB Controller";
+
+#define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045
+static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB controller";
+
+#define PCI_OHCI_DEVICEID_NEC 0x00351033
+static const char *ohci_device_nec = "NEC uPD 9210 USB controller";
+
+#define PCI_OHCI_DEVICEID_NFORCE3 0x00d710de
+static const char *ohci_device_nforce3 = "nVidia nForce3 USB Controller";
+
+#define PCI_OHCI_DEVICEID_USB0670 0x06701095
+static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB controller";
+
+#define PCI_OHCI_DEVICEID_USB0673 0x06731095
+static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB controller";
+
+#define PCI_OHCI_DEVICEID_SIS5571 0x70011039
+static const char *ohci_device_sis5571 = "SiS 5571 USB controller";
+
+#define PCI_OHCI_DEVICEID_KEYLARGO 0x0019106b
+static const char *ohci_device_keylargo = "Apple KeyLargo USB controller";
+
+#define PCI_OHCI_DEVICEID_PCIO2USB 0x1103108e
+static const char *ohci_device_pcio2usb = "Sun PCIO-2 USB controller";
+
+static const char *ohci_device_generic = "OHCI (generic) USB controller";
+
+#define PCI_OHCI_BASE_REG 0x10
+
+
+static device_attach_t ohci_pci_attach;
+static device_detach_t ohci_pci_detach;
+static device_suspend_t ohci_pci_suspend;
+static device_resume_t ohci_pci_resume;
+
+static int
+ohci_pci_suspend(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return err;
+ ohci_power(PWR_SUSPEND, sc);
+
+ return 0;
+}
+
+static int
+ohci_pci_resume(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+
+ ohci_power(PWR_RESUME, sc);
+ bus_generic_resume(self);
+
+ return 0;
+}
+
+static const char *
+ohci_pci_match(device_t self)
+{
+ u_int32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case PCI_OHCI_DEVICEID_ALADDIN_V:
+ return (ohci_device_aladdin_v);
+ case PCI_OHCI_DEVICEID_AMD756:
+ return (ohci_device_amd756);
+ case PCI_OHCI_DEVICEID_AMD766:
+ return (ohci_device_amd766);
+ case PCI_OHCI_DEVICEID_SB400_1:
+ case PCI_OHCI_DEVICEID_SB400_2:
+ return (ohci_device_sb400);
+ case PCI_OHCI_DEVICEID_USB0670:
+ return (ohci_device_usb0670);
+ case PCI_OHCI_DEVICEID_USB0673:
+ return (ohci_device_usb0673);
+ case PCI_OHCI_DEVICEID_FIRELINK:
+ return (ohci_device_firelink);
+ case PCI_OHCI_DEVICEID_NEC:
+ return (ohci_device_nec);
+ case PCI_OHCI_DEVICEID_NFORCE3:
+ return (ohci_device_nforce3);
+ case PCI_OHCI_DEVICEID_SIS5571:
+ return (ohci_device_sis5571);
+ case PCI_OHCI_DEVICEID_KEYLARGO:
+ return (ohci_device_keylargo);
+ case PCI_OHCI_DEVICEID_PCIO2USB:
+ return (ohci_device_pcio2usb);
+ default:
+ if (pci_get_class(self) == PCIC_SERIALBUS
+ && pci_get_subclass(self) == PCIS_SERIALBUS_USB
+ && pci_get_progif(self) == PCI_INTERFACE_OHCI) {
+ return (ohci_device_generic);
+ }
+ }
+
+ return NULL; /* dunno */
+}
+
+static int
+ohci_pci_probe(device_t self)
+{
+ const char *desc = ohci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return BUS_PROBE_DEFAULT;
+ } else {
+ return ENXIO;
+ }
+}
+
+static int
+ohci_pci_attach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ int err;
+ int rid;
+
+ /* XXX where does it say so in the spec? */
+ sc->sc_bus.usbrev = USBREV_1_0;
+
+ pci_enable_busmaster(self);
+
+ /*
+ * Some Sun PCIO-2 USB controllers have their intpin register
+ * bogusly set to 0, although it should be 4. Correct that.
+ */
+ if (pci_get_devid(self) == PCI_OHCI_DEVICEID_PCIO2USB &&
+ pci_get_intpin(self) == 0)
+ pci_set_intpin(self, 4);
+
+ rid = PCI_CBMEM;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->io_res) {
+ device_printf(self, "Could not map memory\n");
+ return ENXIO;
+ }
+ sc->iot = rman_get_bustag(sc->io_res);
+ sc->ioh = rman_get_bushandle(sc->io_res);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ ohci_pci_detach(self);
+ return ENXIO;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ ohci_pci_detach(self);
+ return ENOMEM;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ /* ohci_pci_match will never return NULL if ohci_pci_probe succeeded */
+ device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_OHCI_VENDORID_ACERLABS:
+ sprintf(sc->sc_vendor, "AcerLabs");
+ break;
+ case PCI_OHCI_VENDORID_AMD:
+ sprintf(sc->sc_vendor, "AMD");
+ break;
+ case PCI_OHCI_VENDORID_APPLE:
+ sprintf(sc->sc_vendor, "Apple");
+ break;
+ case PCI_OHCI_VENDORID_ATI:
+ sprintf(sc->sc_vendor, "ATI");
+ break;
+ case PCI_OHCI_VENDORID_CMDTECH:
+ sprintf(sc->sc_vendor, "CMDTECH");
+ break;
+ case PCI_OHCI_VENDORID_NEC:
+ sprintf(sc->sc_vendor, "NEC");
+ break;
+ case PCI_OHCI_VENDORID_NVIDIA:
+ case PCI_OHCI_VENDORID_NVIDIA2:
+ sprintf(sc->sc_vendor, "nVidia");
+ break;
+ case PCI_OHCI_VENDORID_OPTI:
+ sprintf(sc->sc_vendor, "OPTi");
+ break;
+ case PCI_OHCI_VENDORID_SIS:
+ sprintf(sc->sc_vendor, "SiS");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New OHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO, NULL, ohci_intr,
+ sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ ohci_pci_detach(self);
+ return ENXIO;
+ }
+
+ /* Allocate a parent dma tag for DMA maps */
+ err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ NULL, NULL, &sc->sc_bus.parent_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate parent DMA tag (%d)\n",
+ err);
+ ohci_pci_detach(self);
+ return ENXIO;
+ }
+ /* Allocate a dma tag for transfer buffers */
+ err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate transfer tag (%d)\n",
+ err);
+ ohci_pci_detach(self);
+ return ENXIO;
+ }
+
+ err = ohci_init(sc);
+ if (!err) {
+ sc->sc_flags |= OHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+
+ if (err) {
+ device_printf(self, "USB init failed\n");
+ ohci_pci_detach(self);
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+ohci_pci_detach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+
+ if (sc->sc_flags & OHCI_SCFLG_DONEINIT) {
+ ohci_detach(sc, 0);
+ sc->sc_flags &= ~OHCI_SCFLG_DONEINIT;
+ }
+
+ if (sc->sc_bus.parent_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.parent_dmatag);
+ if (sc->sc_bus.buffer_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag);
+
+ if (sc->irq_res && sc->ih) {
+ int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ if (sc->sc_bus.bdev) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (sc->io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM,
+ sc->io_res);
+ sc->io_res = NULL;
+ sc->iot = 0;
+ sc->ioh = 0;
+ }
+ return 0;
+}
+
+static device_method_t ohci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ohci_pci_probe),
+ DEVMETHOD(device_attach, ohci_pci_attach),
+ DEVMETHOD(device_detach, ohci_pci_detach),
+ DEVMETHOD(device_suspend, ohci_pci_suspend),
+ DEVMETHOD(device_resume, ohci_pci_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ohci_driver = {
+ "ohci",
+ ohci_methods,
+ sizeof(ohci_softc_t),
+};
+
+static devclass_t ohci_devclass;
+
+DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0);
+DRIVER_MODULE(ohci, cardbus, ohci_driver, ohci_devclass, 0, 0);
+MODULE_DEPEND(ohci, usb, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/ohcireg.h b/sys/legacy/dev/usb/ohcireg.h
new file mode 100644
index 0000000..429beb8
--- /dev/null
+++ b/sys/legacy/dev/usb/ohcireg.h
@@ -0,0 +1,250 @@
+/* $NetBSD: ohcireg.h,v 1.17 2000/04/01 09:27:35 augustss Exp $ */
+/* $FreeBSD$ */
+
+
+/*-
+ * 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.
+ */
+
+#ifndef _DEV_PCI_OHCIREG_H_
+#define _DEV_PCI_OHCIREG_H_
+
+/*** PCI config registers ***/
+
+#define PCI_CBMEM 0x10 /* configuration base memory */
+
+#define PCI_INTERFACE_OHCI 0x10
+
+/*** OHCI registers */
+
+#define OHCI_REVISION 0x00 /* OHCI revision # */
+#define OHCI_REV_LO(rev) ((rev)&0xf)
+#define OHCI_REV_HI(rev) (((rev)>>4)&0xf)
+#define OHCI_REV_LEGACY(rev) ((rev) & 0x100)
+
+#define OHCI_CONTROL 0x04
+#define OHCI_CBSR_MASK 0x00000003 /* Control/Bulk Service Ratio */
+#define OHCI_RATIO_1_1 0x00000000
+#define OHCI_RATIO_1_2 0x00000001
+#define OHCI_RATIO_1_3 0x00000002
+#define OHCI_RATIO_1_4 0x00000003
+#define OHCI_PLE 0x00000004 /* Periodic List Enable */
+#define OHCI_IE 0x00000008 /* Isochronous Enable */
+#define OHCI_CLE 0x00000010 /* Control List Enable */
+#define OHCI_BLE 0x00000020 /* Bulk List Enable */
+#define OHCI_HCFS_MASK 0x000000c0 /* HostControllerFunctionalState */
+#define OHCI_HCFS_RESET 0x00000000
+#define OHCI_HCFS_RESUME 0x00000040
+#define OHCI_HCFS_OPERATIONAL 0x00000080
+#define OHCI_HCFS_SUSPEND 0x000000c0
+#define OHCI_IR 0x00000100 /* Interrupt Routing */
+#define OHCI_RWC 0x00000200 /* Remote Wakeup Connected */
+#define OHCI_RWE 0x00000400 /* Remote Wakeup Enabled */
+#define OHCI_COMMAND_STATUS 0x08
+#define OHCI_HCR 0x00000001 /* Host Controller Reset */
+#define OHCI_CLF 0x00000002 /* Control List Filled */
+#define OHCI_BLF 0x00000004 /* Bulk List Filled */
+#define OHCI_OCR 0x00000008 /* Ownership Change Request */
+#define OHCI_SOC_MASK 0x00030000 /* Scheduling Overrun Count */
+#define OHCI_INTERRUPT_STATUS 0x0c
+#define OHCI_SO 0x00000001 /* Scheduling Overrun */
+#define OHCI_WDH 0x00000002 /* Writeback Done Head */
+#define OHCI_SF 0x00000004 /* Start of Frame */
+#define OHCI_RD 0x00000008 /* Resume Detected */
+#define OHCI_UE 0x00000010 /* Unrecoverable Error */
+#define OHCI_FNO 0x00000020 /* Frame Number Overflow */
+#define OHCI_RHSC 0x00000040 /* Root Hub Status Change */
+#define OHCI_OC 0x40000000 /* Ownership Change */
+#define OHCI_MIE 0x80000000 /* Master Interrupt Enable */
+#define OHCI_INTERRUPT_ENABLE 0x10
+#define OHCI_INTERRUPT_DISABLE 0x14
+#define OHCI_HCCA 0x18
+#define OHCI_PERIOD_CURRENT_ED 0x1c
+#define OHCI_CONTROL_HEAD_ED 0x20
+#define OHCI_CONTROL_CURRENT_ED 0x24
+#define OHCI_BULK_HEAD_ED 0x28
+#define OHCI_BULK_CURRENT_ED 0x2c
+#define OHCI_DONE_HEAD 0x30
+#define OHCI_FM_INTERVAL 0x34
+#define OHCI_GET_IVAL(s) ((s) & 0x3fff)
+#define OHCI_GET_FSMPS(s) (((s) >> 16) & 0x7fff)
+#define OHCI_FIT 0x80000000
+#define OHCI_FM_REMAINING 0x38
+#define OHCI_FM_NUMBER 0x3c
+#define OHCI_PERIODIC_START 0x40
+#define OHCI_LS_THRESHOLD 0x44
+#define OHCI_RH_DESCRIPTOR_A 0x48
+#define OHCI_GET_NDP(s) ((s) & 0xff)
+#define OHCI_PSM 0x0100 /* Power Switching Mode */
+#define OHCI_NPS 0x0200 /* No Power Switching */
+#define OHCI_DT 0x0400 /* Device Type */
+#define OHCI_OCPM 0x0800 /* Overcurrent Protection Mode */
+#define OHCI_NOCP 0x1000 /* No Overcurrent Protection */
+#define OHCI_GET_POTPGT(s) ((s) >> 24)
+#define OHCI_RH_DESCRIPTOR_B 0x4c
+#define OHCI_RH_STATUS 0x50
+#define OHCI_LPS 0x00000001 /* Local Power Status */
+#define OHCI_OCI 0x00000002 /* OverCurrent Indicator */
+#define OHCI_DRWE 0x00008000 /* Device Remote Wakeup Enable */
+#define OHCI_LPSC 0x00010000 /* Local Power Status Change */
+#define OHCI_CCIC 0x00020000 /* OverCurrent Indicator Change */
+#define OHCI_CRWE 0x80000000 /* Clear Remote Wakeup Enable */
+#define OHCI_RH_PORT_STATUS(n) (0x50 + (n)*4) /* 1 based indexing */
+
+#define OHCI_LES (OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE)
+#define OHCI_ALL_INTRS (OHCI_SO | OHCI_WDH | OHCI_SF | OHCI_RD | OHCI_UE | \
+ OHCI_FNO | OHCI_RHSC | OHCI_OC)
+#define OHCI_NORMAL_INTRS (OHCI_WDH | OHCI_RD | OHCI_UE | OHCI_RHSC)
+
+#define OHCI_FSMPS(i) (((i-210)*6/7) << 16)
+#define OHCI_PERIODIC(i) ((i)*9/10)
+
+typedef u_int32_t ohci_physaddr_t;
+
+#define OHCI_NO_INTRS 32
+struct ohci_hcca {
+ ohci_physaddr_t hcca_interrupt_table[OHCI_NO_INTRS];
+ u_int32_t hcca_frame_number;
+ ohci_physaddr_t hcca_done_head;
+#define OHCI_DONE_INTRS 1
+};
+#define OHCI_HCCA_SIZE 256
+#define OHCI_HCCA_ALIGN 256
+
+#define OHCI_PAGE_SIZE 0x1000
+#define OHCI_PAGE(x) ((x) &~ 0xfff)
+#define OHCI_PAGE_OFFSET(x) ((x) & 0xfff)
+#define OHCI_PAGE_MASK(x) ((x) & 0xfff)
+
+typedef struct {
+ u_int32_t ed_flags;
+#define OHCI_ED_GET_FA(s) ((s) & 0x7f)
+#define OHCI_ED_ADDRMASK 0x0000007f
+#define OHCI_ED_SET_FA(s) (s)
+#define OHCI_ED_GET_EN(s) (((s) >> 7) & 0xf)
+#define OHCI_ED_SET_EN(s) ((s) << 7)
+#define OHCI_ED_DIR_MASK 0x00001800
+#define OHCI_ED_DIR_TD 0x00000000
+#define OHCI_ED_DIR_OUT 0x00000800
+#define OHCI_ED_DIR_IN 0x00001000
+#define OHCI_ED_SPEED 0x00002000
+#define OHCI_ED_SKIP 0x00004000
+#define OHCI_ED_FORMAT_GEN 0x00000000
+#define OHCI_ED_FORMAT_ISO 0x00008000
+#define OHCI_ED_GET_MAXP(s) (((s) >> 16) & 0x07ff)
+#define OHCI_ED_SET_MAXP(s) ((s) << 16)
+#define OHCI_ED_MAXPMASK (0x7ff << 16)
+ ohci_physaddr_t ed_tailp;
+ ohci_physaddr_t ed_headp;
+#define OHCI_HALTED 0x00000001
+#define OHCI_TOGGLECARRY 0x00000002
+#define OHCI_HEADMASK 0xfffffffc
+ ohci_physaddr_t ed_nexted;
+} ohci_ed_t;
+/* #define OHCI_ED_SIZE 16 */
+#define OHCI_ED_ALIGN 16
+
+typedef struct {
+ u_int32_t td_flags;
+#define OHCI_TD_R 0x00040000 /* Buffer Rounding */
+#define OHCI_TD_DP_MASK 0x00180000 /* Direction / PID */
+#define OHCI_TD_SETUP 0x00000000
+#define OHCI_TD_OUT 0x00080000
+#define OHCI_TD_IN 0x00100000
+#define OHCI_TD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */
+#define OHCI_TD_SET_DI(x) ((x) << 21)
+#define OHCI_TD_NOINTR 0x00e00000
+#define OHCI_TD_INTR_MASK 0x00e00000
+#define OHCI_TD_TOGGLE_CARRY 0x00000000
+#define OHCI_TD_TOGGLE_0 0x02000000
+#define OHCI_TD_TOGGLE_1 0x03000000
+#define OHCI_TD_TOGGLE_MASK 0x03000000
+#define OHCI_TD_GET_EC(x) (((x) >> 26) & 3) /* Error Count */
+#define OHCI_TD_GET_CC(x) ((x) >> 28) /* Condition Code */
+#define OHCI_TD_NOCC 0xf0000000
+ ohci_physaddr_t td_cbp; /* Current Buffer Pointer */
+ ohci_physaddr_t td_nexttd; /* Next TD */
+ ohci_physaddr_t td_be; /* Buffer End */
+} ohci_td_t;
+/* #define OHCI_TD_SIZE 16 */
+#define OHCI_TD_ALIGN 16
+
+#define OHCI_ITD_NOFFSET 8
+typedef struct {
+ u_int32_t itd_flags;
+#define OHCI_ITD_GET_SF(x) ((x) & 0x0000ffff)
+#define OHCI_ITD_SET_SF(x) ((x) & 0xffff)
+#define OHCI_ITD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */
+#define OHCI_ITD_SET_DI(x) ((x) << 21)
+#define OHCI_ITD_NOINTR 0x00e00000
+#define OHCI_ITD_GET_FC(x) ((((x) >> 24) & 7)+1) /* Frame Count */
+#define OHCI_ITD_SET_FC(x) (((x)-1) << 24)
+#define OHCI_ITD_GET_CC(x) ((x) >> 28) /* Condition Code */
+#define OHCI_ITD_NOCC 0xf0000000
+ ohci_physaddr_t itd_bp0; /* Buffer Page 0 */
+ ohci_physaddr_t itd_nextitd; /* Next ITD */
+ ohci_physaddr_t itd_be; /* Buffer End */
+ u_int16_t itd_offset[OHCI_ITD_NOFFSET]; /* Buffer offsets */
+#define itd_pswn itd_offset /* Packet Status Word*/
+#define OHCI_ITD_PAGE_SELECT 0x00001000
+#define OHCI_ITD_MK_OFFS(page, off) \
+ (0xe000 | ((page) ? OHCI_ITD_PAGE_SELECT : 0) | ((off) & 0xfff))
+#define OHCI_ITD_PSW_LENGTH(x) ((x) & 0xfff) /* Transfer length */
+#define OHCI_ITD_PSW_GET_CC(x) ((x) >> 12) /* Condition Code */
+} ohci_itd_t;
+/* #define OHCI_ITD_SIZE 32 */
+#define OHCI_ITD_ALIGN 32
+
+
+#define OHCI_CC_NO_ERROR 0
+#define OHCI_CC_CRC 1
+#define OHCI_CC_BIT_STUFFING 2
+#define OHCI_CC_DATA_TOGGLE_MISMATCH 3
+#define OHCI_CC_STALL 4
+#define OHCI_CC_DEVICE_NOT_RESPONDING 5
+#define OHCI_CC_PID_CHECK_FAILURE 6
+#define OHCI_CC_UNEXPECTED_PID 7
+#define OHCI_CC_DATA_OVERRUN 8
+#define OHCI_CC_DATA_UNDERRUN 9
+#define OHCI_CC_BUFFER_OVERRUN 12
+#define OHCI_CC_BUFFER_UNDERRUN 13
+#define OHCI_CC_NOT_ACCESSED 15
+
+/* Some delay needed when changing certain registers. */
+#define OHCI_ENABLE_POWER_DELAY 5
+#define OHCI_READ_DESC_DELAY 5
+
+#endif /* _DEV_PCI_OHCIREG_H_ */
diff --git a/sys/legacy/dev/usb/ohcivar.h b/sys/legacy/dev/usb/ohcivar.h
new file mode 100644
index 0000000..48f99e7
--- /dev/null
+++ b/sys/legacy/dev/usb/ohcivar.h
@@ -0,0 +1,164 @@
+/* $NetBSD: ohcivar.h,v 1.30 2001/12/31 12:20:35 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+typedef struct ohci_soft_ed {
+ ohci_ed_t ed;
+ struct ohci_soft_ed *next;
+ ohci_physaddr_t physaddr;
+} ohci_soft_ed_t;
+#define OHCI_SED_SIZE ((sizeof (struct ohci_soft_ed) + OHCI_ED_ALIGN - 1) / OHCI_ED_ALIGN * OHCI_ED_ALIGN)
+#define OHCI_SED_CHUNK (PAGE_SIZE / OHCI_SED_SIZE)
+
+typedef struct ohci_soft_td {
+ ohci_td_t td;
+ struct ohci_soft_td *nexttd; /* mirrors nexttd in TD */
+ struct ohci_soft_td *dnext; /* next in done list */
+ ohci_physaddr_t physaddr;
+ LIST_ENTRY(ohci_soft_td) hnext;
+ usbd_xfer_handle xfer;
+ u_int16_t len;
+ u_int16_t flags;
+#define OHCI_CALL_DONE 0x0001
+#define OHCI_ADD_LEN 0x0002
+} ohci_soft_td_t;
+#define OHCI_STD_SIZE ((sizeof (struct ohci_soft_td) + OHCI_TD_ALIGN - 1) / OHCI_TD_ALIGN * OHCI_TD_ALIGN)
+#define OHCI_STD_CHUNK (PAGE_SIZE / OHCI_STD_SIZE)
+
+typedef struct ohci_soft_itd {
+ ohci_itd_t itd;
+ struct ohci_soft_itd *nextitd; /* mirrors nexttd in ITD */
+ struct ohci_soft_itd *dnext; /* next in done list */
+ ohci_physaddr_t physaddr;
+ LIST_ENTRY(ohci_soft_itd) hnext;
+ usbd_xfer_handle xfer;
+ u_int16_t flags;
+#define OHCI_ITD_ACTIVE 0x0010 /* Hardware op in progress */
+#define OHCI_ITD_INTFIN 0x0020 /* Hw completion interrupt seen.*/
+#ifdef DIAGNOSTIC
+ char isdone;
+#endif
+} ohci_soft_itd_t;
+#define OHCI_SITD_SIZE ((sizeof (struct ohci_soft_itd) + OHCI_ITD_ALIGN - 1) / OHCI_ITD_ALIGN * OHCI_ITD_ALIGN)
+#define OHCI_SITD_CHUNK (PAGE_SIZE / OHCI_SITD_SIZE)
+
+#define OHCI_NO_EDS (2*OHCI_NO_INTRS-1)
+
+#define OHCI_HASH_SIZE 128
+
+#define OHCI_SCFLG_DONEINIT 0x0001 /* ohci_init() done. */
+
+typedef struct ohci_softc {
+ struct usbd_bus sc_bus; /* base device */
+ int sc_flags;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t sc_size;
+
+ void *ih;
+
+ struct resource *io_res;
+ struct resource *irq_res;
+
+ usb_dma_t sc_hccadma;
+ struct ohci_hcca *sc_hcca;
+ ohci_soft_ed_t *sc_eds[OHCI_NO_EDS];
+ u_int sc_bws[OHCI_NO_INTRS];
+
+ u_int32_t sc_eintrs; /* enabled interrupts */
+
+ ohci_soft_ed_t *sc_isoc_head;
+ ohci_soft_ed_t *sc_ctrl_head;
+ ohci_soft_ed_t *sc_bulk_head;
+
+ LIST_HEAD(, ohci_soft_td) sc_hash_tds[OHCI_HASH_SIZE];
+ LIST_HEAD(, ohci_soft_itd) sc_hash_itds[OHCI_HASH_SIZE];
+
+ int sc_noport;
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+
+#ifdef USB_USE_SOFTINTR
+ char sc_softwake;
+#endif /* USB_USE_SOFTINTR */
+
+ ohci_soft_ed_t *sc_freeeds;
+ ohci_soft_td_t *sc_freetds;
+ ohci_soft_itd_t *sc_freeitds;
+
+ STAILQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */
+
+ usbd_xfer_handle sc_intrxfer;
+
+ ohci_soft_itd_t *sc_sidone;
+ ohci_soft_td_t *sc_sdone;
+
+ char sc_vendor[16];
+ int sc_id_vendor;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ void *sc_powerhook; /* cookie from power hook */
+ void *sc_shutdownhook; /* cookie from shutdown hook */
+#endif
+ u_int32_t sc_control; /* Preserved during suspend/standby */
+ u_int32_t sc_intre;
+
+ u_int sc_overrun_cnt;
+ struct timeval sc_overrun_ntc;
+
+ struct callout sc_tmo_rhsc;
+ char sc_dying;
+} ohci_softc_t;
+
+struct ohci_xfer {
+ struct usbd_xfer xfer;
+ struct usb_task abort_task;
+ u_int32_t ohci_xfer_flags;
+};
+#define OHCI_XFER_ABORTING 0x01 /* xfer is aborting. */
+#define OHCI_XFER_ABORTWAIT 0x02 /* abort completion is being awaited. */
+
+#define OXFER(xfer) ((struct ohci_xfer *)(xfer))
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+usbd_status ohci_init(ohci_softc_t *);
+void ohci_intr(void *);
+int ohci_detach(ohci_softc_t *, int);
+void ohci_shutdown(void *v);
+void ohci_power(int state, void *priv);
diff --git a/sys/legacy/dev/usb/rio500_usb.h b/sys/legacy/dev/usb/rio500_usb.h
new file mode 100644
index 0000000..5b53e2c
--- /dev/null
+++ b/sys/legacy/dev/usb/rio500_usb.h
@@ -0,0 +1,48 @@
+/*-
+ ----------------------------------------------------------------------
+
+ Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted under any licence of your choise which
+ meets the open source licence definiton
+ http://www.opensource.org/opd.html such as the GNU licence or the
+ BSD licence.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License or the BSD license for more details.
+
+ ----------------------------------------------------------------------
+
+ Modified for FreeBSD by Iwasa Kazmi <kzmi@ca2.so-net.ne.jp>
+
+ ---------------------------------------------------------------------- */
+
+/* $FreeBSD$ */
+
+#include <sys/ioccom.h>
+#ifndef USB_VENDOR_DIAMOND
+#define USB_VENDOR_DIAMOND 0x841
+#endif
+#ifndef USB_PRODUCT_DIAMOND_RIO500USB
+#define USB_PRODUCT_DIAMOND_RIO500USB 0x1
+#endif
+
+struct RioCommand
+{
+ uint16_t length;
+ int request;
+ int requesttype;
+ int value;
+ int index;
+ void *buffer;
+ int timeout;
+};
+
+#define RIO_SEND_COMMAND _IOWR('U', 200, struct RioCommand)
+#define RIO_RECV_COMMAND _IOWR('U', 201, struct RioCommand)
+
+#define RIO_DIR_OUT 0x0
+#define RIO_DIR_IN 0x1
diff --git a/sys/legacy/dev/usb/rt2573_ucode.h b/sys/legacy/dev/usb/rt2573_ucode.h
new file mode 100644
index 0000000..f2040f3
--- /dev/null
+++ b/sys/legacy/dev/usb/rt2573_ucode.h
@@ -0,0 +1,213 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005-2006, Ralink Technology, Corp.
+ * Paul Lin <paul_lin@ralinktech.com.tw>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This file contains the loadable 8051 microcode for the Ralink RT2573
+ * chipset.
+ */
+
+static const uint8_t rt2573_ucode[] = {
+ 0x02, 0x13, 0x25, 0x12, 0x10, 0xd9, 0x02, 0x12, 0x58, 0x02, 0x13,
+ 0x58, 0x02, 0x13, 0x5a, 0xc0, 0xd0, 0x75, 0xd0, 0x18, 0x12, 0x13,
+ 0x5c, 0xd0, 0xd0, 0x22, 0x02, 0x14, 0x5c, 0x02, 0x14, 0xe7, 0xed,
+ 0x4c, 0x70, 0x44, 0x90, 0x01, 0xa8, 0x74, 0x80, 0xf0, 0xef, 0x30,
+ 0xe5, 0x07, 0xe4, 0x90, 0x00, 0x0f, 0xf0, 0x80, 0x2c, 0xe5, 0x40,
+ 0x24, 0xc0, 0x60, 0x13, 0x24, 0xc0, 0x60, 0x16, 0x24, 0xc0, 0x60,
+ 0x19, 0x24, 0xc0, 0x70, 0x1a, 0xe4, 0x90, 0x00, 0x0b, 0xf0, 0x80,
+ 0x13, 0xe4, 0x90, 0x00, 0x13, 0xf0, 0x80, 0x0c, 0xe4, 0x90, 0x00,
+ 0x1b, 0xf0, 0x80, 0x05, 0xe4, 0x90, 0x00, 0x23, 0xf0, 0xe4, 0x90,
+ 0x01, 0xa8, 0xf0, 0xd3, 0x22, 0x90, 0x02, 0x02, 0xed, 0xf0, 0x90,
+ 0x02, 0x01, 0xef, 0xf0, 0xd3, 0x22, 0xef, 0x24, 0xc0, 0x60, 0x1f,
+ 0x24, 0xc0, 0x60, 0x2e, 0x24, 0xc0, 0x60, 0x3d, 0x24, 0xc0, 0x70,
+ 0x53, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90,
+ 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00, 0x08, 0x80, 0x37, 0x90, 0x00,
+ 0x13, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x11, 0xe0,
+ 0xfe, 0x90, 0x00, 0x10, 0x80, 0x24, 0x90, 0x00, 0x1b, 0xe0, 0x30,
+ 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00,
+ 0x18, 0x80, 0x11, 0x90, 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x02, 0xc3,
+ 0x22, 0x90, 0x00, 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd,
+ 0xee, 0xf5, 0x37, 0xed, 0xf5, 0x38, 0xd3, 0x22, 0x30, 0x09, 0x20,
+ 0x20, 0x04, 0x0b, 0x90, 0x02, 0x08, 0xe0, 0x54, 0x0f, 0x70, 0x03,
+ 0x02, 0x12, 0x57, 0xc2, 0x09, 0x90, 0x02, 0x00, 0xe0, 0x44, 0x04,
+ 0xf0, 0x74, 0x04, 0x12, 0x0c, 0x3a, 0xc2, 0x04, 0xc2, 0x07, 0x90,
+ 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03,
+ 0x26, 0xe0, 0x20, 0xe2, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x08,
+ 0xe0, 0x70, 0x1b, 0x20, 0x07, 0x03, 0x02, 0x12, 0x57, 0x90, 0x03,
+ 0x12, 0xe0, 0x64, 0x22, 0x60, 0x03, 0x02, 0x12, 0x57, 0xd2, 0x09,
+ 0xc2, 0x07, 0x74, 0x02, 0x12, 0x0c, 0x3a, 0x22, 0x90, 0x02, 0x03,
+ 0xe0, 0x30, 0xe4, 0x47, 0x20, 0x06, 0x44, 0xe5, 0x3c, 0x60, 0x34,
+ 0xe5, 0x40, 0x24, 0xc0, 0x60, 0x14, 0x24, 0xc0, 0x60, 0x18, 0x24,
+ 0xc0, 0x60, 0x1c, 0x24, 0xc0, 0x70, 0x22, 0x90, 0x00, 0x0b, 0xe0,
+ 0x30, 0xe1, 0x1b, 0x22, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x13,
+ 0x22, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x0b, 0x22, 0x90, 0x00,
+ 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x03,
+ 0x74, 0x01, 0xf0, 0x00, 0xe0, 0x54, 0xc0, 0xf5, 0x40, 0xe5, 0x40,
+ 0x24, 0xc0, 0x60, 0x20, 0x24, 0xc0, 0x60, 0x30, 0x24, 0xc0, 0x60,
+ 0x40, 0x24, 0xc0, 0x70, 0x56, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1,
+ 0x03, 0x02, 0x12, 0x57, 0x90, 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00,
+ 0x08, 0x80, 0x3a, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x03, 0x02,
+ 0x12, 0x57, 0x90, 0x00, 0x11, 0xe0, 0xfe, 0x90, 0x00, 0x10, 0x80,
+ 0x26, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57,
+ 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00, 0x18, 0x80, 0x12, 0x90,
+ 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x00,
+ 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd, 0xee, 0xf5, 0x37,
+ 0xed, 0xf5, 0x38, 0x90, 0x03, 0x27, 0x74, 0x82, 0xf0, 0x90, 0x02,
+ 0x01, 0xe5, 0x40, 0xf0, 0x90, 0x02, 0x06, 0xe0, 0xf5, 0x3c, 0xc3,
+ 0xe5, 0x38, 0x95, 0x3a, 0xe5, 0x37, 0x95, 0x39, 0x50, 0x21, 0xe5,
+ 0x40, 0x44, 0x05, 0xff, 0xe5, 0x37, 0xa2, 0xe7, 0x13, 0xfc, 0xe5,
+ 0x38, 0x13, 0xfd, 0x12, 0x10, 0x20, 0xe5, 0x3c, 0x30, 0xe2, 0x04,
+ 0xd2, 0x06, 0x80, 0x02, 0xc2, 0x06, 0x53, 0x3c, 0x01, 0x22, 0x30,
+ 0x0b, 0x07, 0xe4, 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02,
+ 0x02, 0x74, 0x20, 0xf0, 0xe5, 0x40, 0x44, 0x01, 0x90, 0x02, 0x01,
+ 0xf0, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6,
+ 0x90, 0x03, 0x27, 0x74, 0x02, 0xf0, 0xaf, 0x40, 0x12, 0x10, 0x74,
+ 0x40, 0xa5, 0x00, 0x80, 0xf6, 0x22, 0x90, 0x7f, 0xf8, 0xe0, 0xb4,
+ 0x02, 0x03, 0x12, 0x16, 0x38, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0,
+ 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03, 0x26, 0xe0, 0x20, 0xe1, 0x07,
+ 0xe5, 0x3b, 0x70, 0x03, 0x02, 0x13, 0x24, 0xe5, 0x3b, 0x70, 0x15,
+ 0x90, 0x03, 0x24, 0xe0, 0x75, 0xf0, 0x40, 0xa4, 0xf5, 0x36, 0x85,
+ 0xf0, 0x35, 0x75, 0x24, 0x83, 0x75, 0x3b, 0x01, 0x80, 0x03, 0x75,
+ 0x24, 0x03, 0xd3, 0xe5, 0x36, 0x95, 0x3a, 0xe5, 0x35, 0x95, 0x39,
+ 0x40, 0x36, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80,
+ 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0,
+ 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x30, 0x0b, 0x07, 0xe4,
+ 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02, 0x02, 0x74, 0x20,
+ 0xf0, 0x90, 0x02, 0x01, 0x74, 0x21, 0xf0, 0x75, 0x24, 0x03, 0x80,
+ 0x3d, 0xe5, 0x35, 0xa2, 0xe7, 0x13, 0xfe, 0xe5, 0x36, 0x13, 0xfd,
+ 0xac, 0x06, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80,
+ 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0,
+ 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x7f, 0x25, 0x12, 0x10,
+ 0x20, 0xe5, 0x36, 0xb5, 0x3a, 0x08, 0xe5, 0x35, 0xb5, 0x39, 0x03,
+ 0x00, 0x80, 0x04, 0xe4, 0xf5, 0x3b, 0x22, 0xc3, 0xe5, 0x36, 0x95,
+ 0x3a, 0xf5, 0x36, 0xe5, 0x35, 0x95, 0x39, 0xf5, 0x35, 0x02, 0x12,
+ 0x96, 0x22, 0x75, 0xa8, 0x0f, 0x90, 0x03, 0x06, 0x74, 0x01, 0xf0,
+ 0x90, 0x03, 0x07, 0xf0, 0x90, 0x03, 0x08, 0x04, 0xf0, 0x90, 0x03,
+ 0x09, 0x74, 0x6c, 0xf0, 0x90, 0x03, 0x0a, 0x74, 0xff, 0xf0, 0x90,
+ 0x03, 0x02, 0x74, 0x1f, 0xf0, 0x90, 0x03, 0x00, 0x74, 0x04, 0xf0,
+ 0x90, 0x03, 0x25, 0x74, 0x31, 0xf0, 0xd2, 0xaf, 0x22, 0x00, 0x22,
+ 0x00, 0x22, 0x90, 0x03, 0x05, 0xe0, 0x30, 0xe0, 0x0b, 0xe0, 0x44,
+ 0x01, 0xf0, 0x30, 0x09, 0x02, 0xd2, 0x04, 0xc2, 0x07, 0x22, 0x8d,
+ 0x24, 0xa9, 0x07, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5,
+ 0x26, 0xa3, 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff,
+ 0xa3, 0xe0, 0xfd, 0xe9, 0x30, 0xe5, 0x14, 0x54, 0xc0, 0x60, 0x05,
+ 0x43, 0x05, 0x03, 0x80, 0x03, 0x53, 0x05, 0xfc, 0xef, 0x54, 0x3f,
+ 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x07, 0x3f, 0x53, 0x05, 0xf0,
+ 0xe5, 0x24, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53,
+ 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28,
+ 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x8f, 0x24, 0xa9,
+ 0x05, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5, 0x26, 0xa3,
+ 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff, 0xa3, 0xe0,
+ 0xfd, 0xe5, 0x24, 0x30, 0xe5, 0x0b, 0x43, 0x05, 0x0f, 0xef, 0x54,
+ 0x3f, 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x05, 0xf0, 0x53, 0x07,
+ 0x3f, 0xe9, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53,
+ 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28,
+ 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x90, 0x7f, 0xfc,
+ 0xe0, 0xf9, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfb,
+ 0xef, 0x30, 0xe5, 0x0b, 0x43, 0x03, 0x0f, 0xec, 0x54, 0x3f, 0x44,
+ 0x40, 0xfc, 0x80, 0x06, 0x53, 0x03, 0xf0, 0x53, 0x04, 0x3f, 0xed,
+ 0x30, 0xe0, 0x07, 0xef, 0x54, 0xc0, 0x60, 0x07, 0x80, 0x0a, 0xef,
+ 0x54, 0xc0, 0x60, 0x05, 0x43, 0x03, 0x10, 0x80, 0x03, 0x53, 0x03,
+ 0xef, 0x90, 0x7f, 0xfc, 0xe9, 0xf0, 0xa3, 0xee, 0xf0, 0xa3, 0xec,
+ 0xf0, 0xa3, 0xeb, 0xf0, 0x22, 0xe5, 0x4b, 0xfd, 0x54, 0x1f, 0x90,
+ 0x7f, 0xf8, 0xf0, 0xe5, 0x4a, 0xf5, 0x09, 0x90, 0x30, 0x38, 0xe0,
+ 0x90, 0x7f, 0xfc, 0xf0, 0x90, 0x30, 0x39, 0xe0, 0x90, 0x7f, 0xfd,
+ 0xf0, 0x90, 0x30, 0x3a, 0xe0, 0x90, 0x7f, 0xfe, 0xf0, 0x90, 0x30,
+ 0x3b, 0xe0, 0x90, 0x7f, 0xff, 0xf0, 0xed, 0x30, 0xe5, 0x0c, 0x54,
+ 0xc0, 0x60, 0x0d, 0x90, 0x7f, 0xf0, 0xe5, 0x47, 0xf0, 0x80, 0x05,
+ 0xe4, 0x90, 0x7f, 0xf0, 0xf0, 0x90, 0x7f, 0xf8, 0xe0, 0x14, 0x60,
+ 0x08, 0x24, 0xfe, 0x60, 0x0d, 0x24, 0x03, 0x80, 0x12, 0xaf, 0x05,
+ 0xad, 0x09, 0x12, 0x13, 0xc5, 0x80, 0x10, 0xaf, 0x05, 0xad, 0x09,
+ 0x12, 0x14, 0x12, 0x80, 0x07, 0xaf, 0x05, 0xad, 0x09, 0x12, 0x13,
+ 0x6f, 0x90, 0x7f, 0xfc, 0xe0, 0x90, 0x30, 0x38, 0xf0, 0x90, 0x7f,
+ 0xfd, 0xe0, 0x90, 0x30, 0x39, 0xf0, 0x90, 0x7f, 0xfe, 0xe0, 0x90,
+ 0x30, 0x3a, 0xf0, 0x90, 0x7f, 0xff, 0xe0, 0x90, 0x30, 0x3b, 0xf0,
+ 0x22, 0xe5, 0x4b, 0x64, 0x01, 0x60, 0x03, 0x02, 0x15, 0x71, 0xf5,
+ 0x4b, 0xe5, 0x44, 0x45, 0x43, 0x70, 0x03, 0x02, 0x15, 0xa0, 0x12,
+ 0x0c, 0x14, 0x12, 0x0b, 0x86, 0x50, 0xfb, 0x90, 0x00, 0x00, 0xe0,
+ 0xf5, 0x25, 0x12, 0x15, 0xb4, 0xc2, 0x92, 0xe4, 0xf5, 0x24, 0xe5,
+ 0x24, 0xc3, 0x95, 0x25, 0x50, 0x49, 0x7e, 0x00, 0x7f, 0x4c, 0x74,
+ 0x40, 0x25, 0x24, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xad, 0x82, 0xfc,
+ 0x75, 0x2b, 0x02, 0x7b, 0x10, 0x12, 0x07, 0x1e, 0xc2, 0x93, 0x12,
+ 0x15, 0xa1, 0x7d, 0xa0, 0x12, 0x15, 0xd0, 0xe5, 0x24, 0x54, 0x0f,
+ 0x24, 0x4c, 0xf8, 0xe6, 0xfd, 0xaf, 0x4b, 0xae, 0x4a, 0x12, 0x15,
+ 0xd8, 0x05, 0x4b, 0xe5, 0x4b, 0x70, 0x02, 0x05, 0x4a, 0x12, 0x0a,
+ 0x5f, 0x05, 0x24, 0xe5, 0x24, 0x54, 0x0f, 0x70, 0xd5, 0xd2, 0x93,
+ 0x80, 0xb0, 0xc3, 0xe5, 0x44, 0x95, 0x25, 0xf5, 0x44, 0xe5, 0x43,
+ 0x94, 0x00, 0xf5, 0x43, 0x02, 0x14, 0xf2, 0x12, 0x15, 0xb4, 0xc2,
+ 0x93, 0xc2, 0x92, 0x12, 0x15, 0xa1, 0x7d, 0x80, 0x12, 0x15, 0xd0,
+ 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55,
+ 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x7d, 0x30, 0xaf, 0x4b,
+ 0xae, 0x4a, 0x12, 0x15, 0xd8, 0x12, 0x0a, 0x5f, 0xd2, 0x93, 0x22,
+ 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55,
+ 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x22, 0xad, 0x47, 0x7f,
+ 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0x7d, 0xff, 0x7f, 0x35, 0x7e,
+ 0x30, 0x12, 0x15, 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x7e, 0x30, 0x12,
+ 0x15, 0xd8, 0x22, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x22,
+ 0x8f, 0x82, 0x8e, 0x83, 0xed, 0xf0, 0x22, 0xe4, 0xfc, 0x90, 0x7f,
+ 0xf0, 0xe0, 0xaf, 0x09, 0x14, 0x60, 0x14, 0x14, 0x60, 0x15, 0x14,
+ 0x60, 0x16, 0x14, 0x60, 0x17, 0x14, 0x60, 0x18, 0x24, 0x05, 0x70,
+ 0x16, 0xe4, 0xfc, 0x80, 0x12, 0x7c, 0x01, 0x80, 0x0e, 0x7c, 0x03,
+ 0x80, 0x0a, 0x7c, 0x07, 0x80, 0x06, 0x7c, 0x0f, 0x80, 0x02, 0x7c,
+ 0x1f, 0xec, 0x6f, 0xf4, 0x54, 0x1f, 0xfc, 0x90, 0x30, 0x34, 0xe0,
+ 0x54, 0xe0, 0x4c, 0xfd, 0xa3, 0xe0, 0xfc, 0x43, 0x04, 0x1f, 0x7f,
+ 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0xad, 0x04, 0x0f, 0x12, 0x15,
+ 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x02, 0x15, 0xd8, 0x02, 0x15, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+ 0x29, 0xe9
+};
diff --git a/sys/legacy/dev/usb/sl811hs.c b/sys/legacy/dev/usb/sl811hs.c
new file mode 100644
index 0000000..3620d5f
--- /dev/null
+++ b/sys/legacy/dev/usb/sl811hs.c
@@ -0,0 +1,1654 @@
+/* $NetBSD: sl811hs.c,v 1.5 2005/02/27 00:27:02 perry Exp $ */
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Tetsuya Isaki.
+ *
+ * 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.
+ */
+
+
+/*
+ * ScanLogic SL811HS/T USB Host Controller
+ */
+/*
+ * !! HIGHLY EXPERIMENTAL CODE !!
+ */
+
+#include <sys/cdefs.h>
+//_RCSID(0, "$NetBSD: sl811hs.c,v 1.5 2005/02/27 00:27:02 perry Exp $");
+
+#include "opt_slhci.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <sys/module.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_port.h>
+#include "usbdevs.h"
+
+#include <dev/usb/sl811hsreg.h>
+#include <dev/usb/sl811hsvar.h>
+
+__FBSDID("$FreeBSD$");
+
+static inline u_int8_t sl11read(struct slhci_softc *, int);
+static inline void sl11write(struct slhci_softc *, int, u_int8_t);
+static inline void sl11read_region(struct slhci_softc *, u_char *, int, int);
+static inline void sl11write_region(struct slhci_softc *, int, u_char *, int);
+
+static void sl11_reset(struct slhci_softc *);
+static void sl11_speed(struct slhci_softc *);
+
+static usbd_status slhci_open(usbd_pipe_handle);
+static void slhci_softintr(void *);
+static void slhci_poll(struct usbd_bus *);
+static void slhci_poll_hub(void *);
+static void slhci_poll_device(void *arg);
+static usbd_status slhci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
+static void slhci_freem(struct usbd_bus *, usb_dma_t *);
+static usbd_xfer_handle slhci_allocx(struct usbd_bus *);
+static void slhci_freex(struct usbd_bus *, usbd_xfer_handle);
+
+static int slhci_str(usb_string_descriptor_t *, int, const char *);
+
+static usbd_status slhci_root_ctrl_transfer(usbd_xfer_handle);
+static usbd_status slhci_root_ctrl_start(usbd_xfer_handle);
+static void slhci_root_ctrl_abort(usbd_xfer_handle);
+static void slhci_root_ctrl_close(usbd_pipe_handle);
+static void slhci_root_ctrl_done(usbd_xfer_handle);
+
+static usbd_status slhci_root_intr_transfer(usbd_xfer_handle);
+static usbd_status slhci_root_intr_start(usbd_xfer_handle);
+static void slhci_root_intr_abort(usbd_xfer_handle);
+static void slhci_root_intr_close(usbd_pipe_handle);
+static void slhci_root_intr_done(usbd_xfer_handle);
+
+static usbd_status slhci_device_ctrl_transfer(usbd_xfer_handle);
+static usbd_status slhci_device_ctrl_start(usbd_xfer_handle);
+static void slhci_device_ctrl_abort(usbd_xfer_handle);
+static void slhci_device_ctrl_close(usbd_pipe_handle);
+static void slhci_device_ctrl_done(usbd_xfer_handle);
+
+static usbd_status slhci_device_intr_transfer(usbd_xfer_handle);
+static usbd_status slhci_device_intr_start(usbd_xfer_handle);
+static void slhci_device_intr_abort(usbd_xfer_handle);
+static void slhci_device_intr_close(usbd_pipe_handle);
+static void slhci_device_intr_done(usbd_xfer_handle);
+
+static usbd_status slhci_device_isoc_transfer(usbd_xfer_handle);
+static usbd_status slhci_device_isoc_start(usbd_xfer_handle);
+static void slhci_device_isoc_abort(usbd_xfer_handle);
+static void slhci_device_isoc_close(usbd_pipe_handle);
+static void slhci_device_isoc_done(usbd_xfer_handle);
+
+static usbd_status slhci_device_bulk_transfer(usbd_xfer_handle);
+static usbd_status slhci_device_bulk_start(usbd_xfer_handle);
+static void slhci_device_bulk_abort(usbd_xfer_handle);
+static void slhci_device_bulk_close(usbd_pipe_handle);
+static void slhci_device_bulk_done(usbd_xfer_handle);
+
+static int slhci_transaction(struct slhci_softc *,
+ usbd_pipe_handle, u_int8_t, int, u_char *, u_int8_t);
+static void slhci_noop(usbd_pipe_handle);
+static void slhci_abort_xfer(usbd_xfer_handle, usbd_status);
+static void slhci_device_clear_toggle(usbd_pipe_handle);
+
+extern int usbdebug;
+
+/* For root hub */
+#define SLHCI_INTR_ENDPT (1)
+
+#ifdef SLHCI_DEBUG
+#define D_TRACE (0x0001) /* function trace */
+#define D_MSG (0x0002) /* debug messages */
+#define D_XFER (0x0004) /* transfer messages (noisy!) */
+#define D_MEM (0x0008) /* memory allocation */
+
+int slhci_debug = D_MSG | D_XFER;
+#define DPRINTF(z,x) if((slhci_debug&(z))!=0)printf x
+void print_req(usb_device_request_t *);
+void print_req_hub(usb_device_request_t *);
+void print_dumpreg(struct slhci_softc *);
+void print_xfer(usbd_xfer_handle);
+#else
+#define DPRINTF(z,x)
+#endif
+
+
+/* XXX: sync with argument */
+static const char *sltypestr [] = {
+ "SL11H/T",
+ "SL811HS/T",
+};
+
+
+struct usbd_bus_methods slhci_bus_methods = {
+ slhci_open,
+ slhci_softintr,
+ slhci_poll,
+ slhci_allocm,
+ slhci_freem,
+ slhci_allocx,
+ slhci_freex,
+};
+
+struct usbd_pipe_methods slhci_root_ctrl_methods = {
+ slhci_root_ctrl_transfer,
+ slhci_root_ctrl_start,
+ slhci_root_ctrl_abort,
+ slhci_root_ctrl_close,
+ slhci_noop,
+ slhci_root_ctrl_done,
+};
+
+struct usbd_pipe_methods slhci_root_intr_methods = {
+ slhci_root_intr_transfer,
+ slhci_root_intr_start,
+ slhci_root_intr_abort,
+ slhci_root_intr_close,
+ slhci_noop,
+ slhci_root_intr_done,
+};
+
+struct usbd_pipe_methods slhci_device_ctrl_methods = {
+ slhci_device_ctrl_transfer,
+ slhci_device_ctrl_start,
+ slhci_device_ctrl_abort,
+ slhci_device_ctrl_close,
+ slhci_noop,
+ slhci_device_ctrl_done,
+};
+
+struct usbd_pipe_methods slhci_device_intr_methods = {
+ slhci_device_intr_transfer,
+ slhci_device_intr_start,
+ slhci_device_intr_abort,
+ slhci_device_intr_close,
+ slhci_device_clear_toggle,
+ slhci_device_intr_done,
+};
+
+struct usbd_pipe_methods slhci_device_isoc_methods = {
+ slhci_device_isoc_transfer,
+ slhci_device_isoc_start,
+ slhci_device_isoc_abort,
+ slhci_device_isoc_close,
+ slhci_noop,
+ slhci_device_isoc_done,
+};
+
+struct usbd_pipe_methods slhci_device_bulk_methods = {
+ slhci_device_bulk_transfer,
+ slhci_device_bulk_start,
+ slhci_device_bulk_abort,
+ slhci_device_bulk_close,
+ slhci_noop,
+ slhci_device_bulk_done,
+};
+
+struct slhci_pipe {
+ struct usbd_pipe pipe;
+};
+
+
+/*
+ * SL811HS Register read/write routine
+ */
+static inline u_int8_t
+sl11read(struct slhci_softc *sc, int reg)
+{
+#if 1
+ int b;
+ DELAY(80);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_ADDR, reg);
+ b = bus_space_read_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_DATA);
+ return b;
+#else
+ outb(0x4000, reg&0xff);
+ return (inb(0x4001)&0xff);
+#endif
+}
+
+static inline void
+sl11write(struct slhci_softc *sc, int reg, u_int8_t data)
+{
+#if 1
+ DELAY(80);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_ADDR, reg);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_DATA, data);
+#else
+ outb(0x4000, reg&0xff);
+ outb(0x4000, data&0xff);
+#endif
+}
+
+static inline void
+sl11read_region(struct slhci_softc *sc, u_char *buf, int reg, int len)
+{
+ int i;
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_ADDR, reg);
+ for (i = 0; i < len; i++)
+ buf[i] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_DATA);
+}
+
+static inline void
+sl11write_region(struct slhci_softc *sc, int reg, u_char *buf, int len)
+{
+ int i;
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_ADDR, reg);
+ for (i = 0; i < len; i++)
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, SL11_IDX_DATA, buf[i]);
+}
+
+/*
+ * USB bus reset. From sl811hs_appnote.pdf, p22
+ */
+static void
+sl11_reset(struct slhci_softc *sc)
+{
+ u_int8_t r;
+
+ DPRINTF(D_TRACE, ("%s() ", __FUNCTION__));
+ // r = sl11read(sc, SL11_CTRL);
+ r = 0;
+ sl11write(sc, SL11_CTRL, r | SL11_CTRL_RESETENGINE);
+ delay_ms(250);
+ sl11write(sc, SL11_CTRL, r | SL11_CTRL_JKSTATE | SL11_CTRL_RESETENGINE); delay_ms(150);
+ sl11write(sc, SL11_CTRL, r | SL11_CTRL_RESETENGINE);
+ delay_ms(10);
+ sl11write(sc, SL11_CTRL, r);
+}
+
+/*
+ * Detect the speed of attached device.
+ */
+static void
+sl11_speed(struct slhci_softc *sc)
+{
+ u_int8_t r;
+
+ sl11write(sc, SL11_ISR, 0xff);
+ r = sl11read(sc, SL11_ISR);
+ if ((r & SL11_ISR_RESET)) {
+ DPRINTF(D_MSG, ("NC "));
+ sl11write(sc, SL11_ISR, SL11_ISR_RESET);
+ sc->sc_connect = 0;
+ }
+
+ if ((sl11read(sc, SL11_ISR) & SL11_ISR_RESET)) {
+ sl11write(sc, SL11_ISR, 0xff);
+ } else {
+ u_int8_t pol = 0, ctrl = 0;
+
+ sc->sc_connect = 1;
+ if (r & SL11_ISR_DATA) {
+ DPRINTF(D_MSG, ("FS "));
+ pol = 0;
+ ctrl = SL11_CTRL_EOF2;
+ sc->sc_fullspeed = 1;
+ } else {
+ DPRINTF(D_MSG, ("LS "));
+ pol = SL811_CSOF_POLARITY;
+ ctrl = SL11_CTRL_LOWSPEED;
+ sc->sc_fullspeed = 0;
+ }
+ sl11write(sc, SL811_CSOF, pol | SL811_CSOF_MASTER | 0x2e);
+ sl11write(sc, SL11_DATA, 0xe0);
+ sl11write(sc, SL11_CTRL, ctrl | SL11_CTRL_ENABLESOF);
+ }
+
+ sl11write(sc, SL11_E0PID, (SL11_PID_SOF << 4) + 0);
+ sl11write(sc, SL11_E0DEV, 0);
+ sl11write(sc, SL11_E0CTRL, SL11_EPCTRL_ARM);
+ delay_ms(30);
+}
+
+/*
+ * If detect some known controller, return the type.
+ * If does not, return -1.
+ */
+int
+sl811hs_find(struct slhci_softc *sc)
+{
+ int rev;
+ sc->sc_sltype = -1;
+
+ rev = sl11read(sc, SL11_REV) >> 4;
+ if (rev >= SLTYPE_SL11H && rev <= SLTYPE_SL811HS_R14)
+ sc->sc_sltype = rev;
+ return sc->sc_sltype;
+}
+
+/*
+ * Attach SL11H/SL811HS. Return 0 if success.
+ */
+int
+slhci_attach(struct slhci_softc *sc)
+{
+ int rev;
+ /* Detect and check the controller type */
+
+ rev = sl811hs_find(sc);
+ if (rev == -1)
+ return -1;
+
+ printf("%s: ScanLogic %s USB Host Controller",
+ device_get_nameunit(sc->sc_bus.bdev), sltypestr[(rev > 0)]);
+ switch (rev) {
+ case SLTYPE_SL11H:
+ break;
+ case SLTYPE_SL811HS_R12:
+ printf(" (rev 1.2)");
+ break;
+ case SLTYPE_SL811HS_R14:
+ printf(" (rev 1.4)");
+ break;
+ default:
+ printf(" (unknown revision)");
+ break;
+ }
+ printf("\n");
+
+
+ /* Initialize sc */
+ sc->sc_bus.usbrev = USBREV_1_1;
+ sc->sc_bus.methods = &slhci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct slhci_pipe);
+ sc->sc_bus.parent_dmatag = NULL; /* XXX */
+ sc->sc_bus.buffer_dmatag = NULL; /* XXX */
+ STAILQ_INIT(&sc->sc_free_xfers);
+
+ usb_callout_init(sc->sc_poll_handle);
+
+ /* Disable interrupt, then wait 40msec */
+ sl11write(sc, SL11_IER, 0x00);
+ delay_ms(40);
+
+ /* Initialize controller */
+ sl11write(sc, SL811_CSOF, SL811_CSOF_MASTER | 0x2e);
+ delay_ms(40);
+
+ sl11write(sc, SL11_ISR, 0xff);
+
+ /* Disable interrupt, then wait 40msec */
+ sl11write(sc, SL11_IER, 0x00);
+
+ /* Reset USB engine */
+ sl11write(sc, SL11_CTRL, SL11_CTRL_JKSTATE| SL11_CTRL_RESETENGINE);
+ delay_ms(40);
+ sl11write(sc, SL11_CTRL, 0x00);
+ delay_ms(10);
+
+ /* USB Bus reset for GET_PORT_STATUS */
+ sl11_reset(sc);
+ sc->sc_flags = SLF_ATTACHED;
+
+ /* Enable interrupt */
+ sl11write(sc, SL11_IER, SL11_IER_INSERT);
+ /* x68k Nereid USB controller needs it */
+ if (sc->sc_enable_intr)
+ sc->sc_enable_intr(sc->sc_arg, INTR_ON);
+#ifdef USB_DEBUG
+ usbdebug = 0;
+#endif
+
+ return 0;
+}
+
+int
+slhci_intr(void *arg)
+{
+ struct slhci_softc *sc = arg;
+ u_int8_t r;
+#ifdef SLHCI_DEBUG
+ char bitbuf[256];
+#endif
+
+
+ if((sc->sc_flags & SLF_ATTACHED) == 0)
+ return 0;
+
+ r = sl11read(sc, SL11_ISR);
+
+
+
+ sl11write(sc, SL11_ISR, SL11_ISR_DATA | SL11_ISR_SOFTIMER);
+
+ if ((r & SL11_ISR_RESET)) {
+ sc->sc_flags |= SLF_RESET;
+ sl11write(sc, SL11_ISR, SL11_ISR_RESET);
+ }
+ if ((r & SL11_ISR_INSERT)) {
+ sc->sc_flags |= SLF_INSERT;
+ sl11write(sc, SL11_ISR, SL11_ISR_INSERT);
+ }
+
+#ifdef SLHCI_DEBUG
+ bitmask_snprintf(r,
+ (sl11read(sc, SL11_CTRL) & SL11_CTRL_SUSPEND)
+ ? "\20\x8""D+\7RESUME\6INSERT\5SOF\4res\3""BABBLE\2USBB\1USBA"
+ : "\20\x8""D+\7RESET\6INSERT\5SOF\4res\3""BABBLE\2USBB\1USBA",
+ bitbuf, sizeof(bitbuf));
+ DPRINTF(D_XFER, ("I=%s ", bitbuf));
+#endif /* SLHCI_DEBUG */
+
+ return 0;
+}
+
+usbd_status
+slhci_open(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ struct slhci_softc *sc = (struct slhci_softc *)dev->bus;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+
+ DPRINTF(D_TRACE, ("slhci_open(addr=%d,ep=%d,scaddr=%d)",
+ dev->address, ed->bEndpointAddress, sc->sc_addr));
+
+ if (dev->address == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &slhci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | SLHCI_INTR_ENDPT:
+ pipe->methods = &slhci_root_intr_methods;
+ break;
+ default:
+ printf("open:endpointErr!\n");
+ return USBD_INVAL;
+ }
+ } else {
+ switch (ed->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ DPRINTF(D_MSG, ("control "));
+ pipe->methods = &slhci_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ DPRINTF(D_MSG, ("interrupt "));
+ pipe->methods = &slhci_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ DPRINTF(D_MSG, ("isochronous "));
+ pipe->methods = &slhci_device_isoc_methods;
+ break;
+ case UE_BULK:
+ DPRINTF(D_MSG, ("bluk "));
+ pipe->methods = &slhci_device_bulk_methods;
+ break;
+ }
+ }
+ return USBD_NORMAL_COMPLETION;
+}
+
+void
+slhci_softintr(void *arg)
+{
+ DPRINTF(D_TRACE, ("%s()", __FUNCTION__));
+}
+
+void
+slhci_poll(struct usbd_bus *bus)
+{
+ DPRINTF(D_TRACE, ("%s()", __FUNCTION__));
+}
+
+/*
+ * Emulation of interrupt transfer for status change endpoint
+ * of root hub.
+ */
+void
+slhci_poll_hub(void *arg)
+{
+ usbd_xfer_handle xfer = arg;
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct slhci_softc *sc = (struct slhci_softc *)pipe->device->bus;
+ int s;
+ u_char *p;
+
+ usb_callout(sc->sc_poll_handle, sc->sc_interval, slhci_poll_hub, xfer);
+
+ /* USB spec 11.13.3 (p.260) */
+ p = xfer->buffer;
+ p[0] = 0;
+ if ((sc->sc_flags & (SLF_INSERT | SLF_RESET))) {
+ p[0] = 2;
+ DPRINTF(D_TRACE, ("!"));
+ }
+
+ /* no change, return NAK */
+ if (p[0] == 0)
+ return;
+
+ xfer->actlen = 1;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ xfer->device->bus->intr_context++;
+ usb_transfer_complete(xfer);
+ xfer->device->bus->intr_context--;
+ splx(s);
+}
+
+usbd_status
+slhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
+{
+ struct slhci_softc *sc = (struct slhci_softc *)bus;
+
+ DPRINTF(D_MEM, ("SLallocm"));
+ return usb_allocmem(&sc->sc_bus, size, 0, dma);
+}
+
+void
+slhci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ struct slhci_softc *sc = (struct slhci_softc *)bus;
+
+ DPRINTF(D_MEM, ("SLfreem"));
+ usb_freemem(&sc->sc_bus, dma);
+}
+
+usbd_xfer_handle
+slhci_allocx(struct usbd_bus *bus)
+{
+ struct slhci_softc *sc = (struct slhci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ DPRINTF(D_MEM, ("SLallocx"));
+
+ xfer = STAILQ_FIRST(&sc->sc_free_xfers);
+ if (xfer) {
+ STAILQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_FREE) {
+ printf("slhci_allocx: xfer=%p not free, 0x%08x\n",
+ xfer, xfer->busy_free);
+ }
+#endif
+ } else {
+ xfer = malloc(sizeof(*xfer), M_USB, M_NOWAIT);
+ }
+
+ if (xfer) {
+ memset(xfer, 0, sizeof(*xfer));
+#ifdef DIAGNOSTIC
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+
+ return xfer;
+}
+
+void
+slhci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct slhci_softc *sc = (struct slhci_softc *)bus;
+
+ DPRINTF(D_MEM, ("SLfreex"));
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("slhci_freex: xfer=%p not busy, 0x%08x\n",
+ xfer, xfer->busy_free);
+ return;
+ }
+ xfer->busy_free = XFER_FREE;
+#endif
+ STAILQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+void
+slhci_noop(usbd_pipe_handle pipe)
+{
+ DPRINTF(D_TRACE, ("%s()", __FUNCTION__));
+}
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+usb_device_descriptor_t slhci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x01, 0x01}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ 0, /* protocol */
+ 64, /* max packet */
+ {USB_VENDOR_SCANLOGIC & 0xff, /* vendor ID (low) */
+ USB_VENDOR_SCANLOGIC >> 8 }, /* vendor ID (high) */
+ {0} /* ? */, /* product ID */
+ {0}, /* device */
+ 1, /* index to manufacturer */
+ 2, /* index to product */
+ 0, /* index to serial number */
+ 1 /* number of configurations */
+};
+
+usb_config_descriptor_t slhci_confd = {
+ USB_CONFIG_DESCRIPTOR_SIZE,
+ UDESC_CONFIG,
+ {USB_CONFIG_DESCRIPTOR_SIZE +
+ USB_INTERFACE_DESCRIPTOR_SIZE +
+ USB_ENDPOINT_DESCRIPTOR_SIZE},
+ 1, /* number of interfaces */
+ 1, /* configuration value */
+ 0, /* index to configuration */
+ UC_SELF_POWERED, /* attributes */
+ 15 /* max current is 30mA... */
+};
+
+usb_interface_descriptor_t slhci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0, /* interface number */
+ 0, /* alternate setting */
+ 1, /* number of endpoint */
+ UICLASS_HUB, /* class */
+ UISUBCLASS_HUB, /* subclass */
+ 0, /* protocol */
+ 0 /* index to interface */
+};
+
+usb_endpoint_descriptor_t slhci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | SLHCI_INTR_ENDPT, /* endpoint address */
+ UE_INTERRUPT, /* attributes */
+ {8}, /* max packet size */
+ 255 /* interval */
+};
+
+usb_hub_descriptor_t slhci_hubd = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 1, /* number of ports */
+ {UHD_PWR_INDIVIDUAL | UHD_OC_NONE, 0}, /* hub characteristics */
+ 20 /* ? */, /* 5:power on to power good */
+ 50, /* 6:maximum current */
+ { 0x00 }, /* both ports are removable */
+ { 0x00 } /* port power control mask */
+};
+
+static int
+slhci_str(usb_string_descriptor_t *p, int l, const char *s)
+{
+ int i;
+
+ if (l == 0)
+ return 0;
+ p->bLength = 2 * strlen(s) + 2;
+ if (l == 1)
+ return 1;
+ p->bDescriptorType = UDESC_STRING;
+ l -= 2;
+ for (i = 0; s[i] && l > 1; i++, l -= 2)
+ USETW2(p->bString[i], 0, s[i]);
+ return 2 * i + 2;
+}
+
+usbd_status
+slhci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status error;
+
+ DPRINTF(D_TRACE, ("SLRCtrans "));
+
+ /* Insert last in queue */
+ error = usb_insert_transfer(xfer);
+ if (error) {
+ DPRINTF(D_MSG, ("usb_insert_transfer returns err! "));
+ return error;
+ }
+
+ /*
+ * Pipe isn't running (otherwise error would be USBD_INPROG),
+ * so start it first.
+ */
+ return slhci_root_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue));
+}
+
+usbd_status
+slhci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ struct slhci_softc *sc = (struct slhci_softc *)xfer->pipe->device->bus;
+ usb_device_request_t *req;
+ int len, value, index, l, s, status;
+ int totlen = 0;
+ void *buf = NULL;
+ usb_port_status_t ps;
+ usbd_status error;
+ char slbuf[50];
+ u_int8_t r;
+
+ DPRINTF(D_TRACE, ("SLRCstart "));
+
+ req = &xfer->request;
+
+ len = UGETW(req->wLength);
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ if (len)
+ buf = xfer->buffer;
+
+#ifdef SLHCI_DEBUG
+ if ((slhci_debug & D_TRACE))
+ print_req_hub(req);
+#endif
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch (C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ DPRINTF(D_MSG, ("UR_CLEAR_FEATURE "));
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ DPRINTF(D_MSG, ("UR_GET_CONFIG "));
+ if (len > 0) {
+ *(u_int8_t *)buf = sc->sc_conf;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ DPRINTF(D_MSG, ("UDESC_DEVICE "));
+ if ((value & 0xff) != 0) {
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &slhci_devd, l);
+ break;
+ case UDESC_CONFIG:
+ DPRINTF(D_MSG, ("UDESC_CONFIG "));
+ if ((value & 0xff) != 0) {
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &slhci_confd, l);
+ buf = (char *)buf + l;
+ len -= l;
+
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &slhci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &slhci_endpd, l);
+ break;
+ case UDESC_STRING:
+ DPRINTF(D_MSG, ("UDESC_STR "));
+ if (len == 0)
+ break;
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ switch (value & 0xff) {
+ case 0:
+ break;
+ case 1: /* Vendor */
+ totlen = slhci_str(buf, len, "ScanLogic");
+ break;
+ case 2: /* Product */
+ snprintf(slbuf, sizeof(slbuf), "%s root hub",
+ sltypestr[sc->sc_sltype>0]);
+ totlen = slhci_str(buf, len, slbuf);
+ break;
+ default:
+ printf("strerr%d ", value & 0xff);
+ break;
+ }
+ break;
+ default:
+ printf("unknownGetDescriptor=%x", value);
+ error = USBD_IOERROR;
+ break;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ /* Get Interface, 9.4.4 */
+ if (len > 0) {
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ /* Get Status from device, 9.4.5 */
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, UDS_SELF_POWERED);
+ totlen = 2;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ /* Get Status from interface, endpoint, 9.4.5 */
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, 0);
+ totlen = 2;
+ }
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ /* Set Address, 9.4.6 */
+ DPRINTF(D_MSG, ("UR_SET_ADDRESS "));
+ if (value >= USB_MAX_DEVICES) {
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ /* Set Configuration, 9.4.7 */
+ DPRINTF(D_MSG, ("UR_SET_CONFIG "));
+ if (value != 0 && value != 1) {
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ /* Set Descriptor, 9.4.8, not supported */
+ DPRINTF(D_MSG, ("UR_SET_DESCRIPTOR,WRITE_DEVICE not supported\n"));
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ /* Set Feature, 9.4.9, not supported */
+ DPRINTF(D_MSG, ("UR_SET_FEATURE not supported\n"));
+ error = USBD_IOERROR;
+ break;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ /* Set Interface, 9.4.10, not supported */
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ /* Synch Frame, 9.4.11, not supported */
+ break;
+
+ /*
+ * Hub specific requests
+ */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ /* Clear Hub Feature, 11.16.2.1, not supported */
+ DPRINTF(D_MSG, ("ClearHubFeature not supported\n"));
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ /* Clear Port Feature, 11.16.2.2 */
+ if (index != 1) {
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ switch (value) {
+ case UHF_PORT_POWER:
+ DPRINTF(D_MSG, ("POWER_OFF "));
+ sc->sc_powerstat = POWER_OFF;
+ /* x68k Nereid USB controller needs it */
+ if (sc->sc_enable_power)
+ sc->sc_enable_power(sc, sc->sc_powerstat);
+ break;
+ case UHF_PORT_SUSPEND:
+ DPRINTF(D_MSG, ("SUSPEND "));
+ sl11write(sc, SL11_CTRL,
+ sl11read(sc, SL11_CTRL) & ~SL11_CTRL_SUSPEND);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ sc->sc_change &= ~UPS_C_CONNECT_STATUS;
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_change &= ~UPS_C_PORT_RESET;
+ break;
+ case UHF_PORT_ENABLE:
+ break;
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ default:
+ printf("ClrPortFeatERR:value=0x%x ", value);
+ error = USBD_IOERROR;
+ break;
+ }
+ //DPRINTF(D_XFER, ("CH=%04x ", sc->sc_change));
+ break;
+ case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER):
+ /* Get Bus State, 11.16.2.3, not supported */
+ /* shall return a STALL... */
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ /* Get Hub Descriptor, 11.16.2.4 */
+ if (value != 0) {
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ l = min(len, USB_HUB_DESCRIPTOR_SIZE);
+ totlen = l;
+ memcpy(buf, &slhci_hubd, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ /* Get Hub Status, 11.16.2.5 */
+ DPRINTF(D_MSG, ("UR_GET_STATUS RCD"));
+ if (len != 4) {
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len);
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ /* Get Port Status, 11.16.2.6 */
+ if (index != 1 || len != 4) {
+ printf("index=%d,len=%d ", index, len);
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ /*
+ * change
+ * o port is always enabled.
+ * o cannot detect over current.
+ */
+ s = splusb();
+ sc->sc_change &= ~(UPS_C_CONNECT_STATUS | UPS_C_PORT_RESET);
+ if ((sc->sc_flags & SLF_INSERT)) {
+ sc->sc_flags &= ~SLF_INSERT;
+ sc->sc_change |= UPS_C_CONNECT_STATUS;
+ }
+ if ((sc->sc_flags & SLF_RESET)) {
+ sc->sc_flags &= ~SLF_RESET;
+ sc->sc_change |= UPS_C_PORT_RESET;
+ }
+ splx(s);
+ /*
+ * XXX It can recognize that device is detached,
+ * while there is sl11_speed() here.
+ */
+ if (sc->sc_change)
+ sl11_speed(sc);
+ /*
+ * status
+ * o port is always enabled.
+ * o cannot detect over current.
+ */
+ status = 0;
+ if (sc->sc_connect)
+ status |= UPS_CURRENT_CONNECT_STATUS | UPS_PORT_ENABLED;
+ r = sl11read(sc, SL11_CTRL);
+ if (r & SL11_CTRL_SUSPEND)
+ status |= UPS_SUSPEND;
+ if (sc->sc_powerstat)
+ status |= UPS_PORT_POWER;
+ if (!sc->sc_fullspeed)
+ status |= UPS_LOW_SPEED;
+
+ //DPRINTF(D_XFER, ("ST=%04x,CH=%04x ", status, sc->sc_change));
+ USETW(ps.wPortStatus, status);
+ USETW(ps.wPortChange, sc->sc_change);
+ l = min(len, sizeof(ps));
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ /* Set Hub Descriptor, 11.16.2.7, not supported */
+ /* STALL ? */
+ error = USBD_IOERROR;
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ /* Set Hub Feature, 11.16.2.8, not supported */
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ /* Set Port Feature, 11.16.2.9 */
+ if (index != 1) {
+ printf("index=%d ", index);
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ switch (value) {
+ case UHF_PORT_RESET:
+ DPRINTF(D_MSG, ("PORT_RESET "));
+ sl11_reset(sc);
+ sl11_speed(sc);
+ sc->sc_change = 0;
+ break;
+ case UHF_PORT_POWER:
+ DPRINTF(D_MSG, ("PORT_POWER "));
+ sc->sc_powerstat = POWER_ON;
+ /* x68k Nereid USB controller needs it */
+ if (sc->sc_enable_power)
+ sc->sc_enable_power(sc, sc->sc_powerstat);
+ delay_ms(25);
+ break;
+ default:
+ printf("SetPortFeatERR=0x%x ", value);
+ error = USBD_IOERROR;
+ break;
+ }
+ break;
+ default:
+ DPRINTF(D_MSG, ("ioerr(UR=%02x,UT=%02x) ",
+ req->bRequest, req->bmRequestType));
+ error = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ error = USBD_NORMAL_COMPLETION;
+ ret:
+ xfer->status = error;
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ return USBD_IN_PROGRESS;
+}
+
+void
+slhci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("SLRCabort "));
+}
+
+void
+slhci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(D_TRACE, ("SLRCclose "));
+}
+
+void
+slhci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("SLRCdone\n"));
+}
+
+static usbd_status
+slhci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status error;
+
+ DPRINTF(D_TRACE, ("SLRItransfer "));
+
+ /* Insert last in queue */
+ error = usb_insert_transfer(xfer);
+ if (error)
+ return error;
+
+ /*
+ * Pipe isn't running (otherwise error would be USBD_INPROG),
+ * start first.
+ */
+ return slhci_root_intr_start(STAILQ_FIRST(&xfer->pipe->queue));
+}
+
+static usbd_status
+slhci_root_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct slhci_softc *sc = (struct slhci_softc *)pipe->device->bus;
+
+ DPRINTF(D_TRACE, ("SLRIstart "));
+
+ sc->sc_interval = MS_TO_TICKS(xfer->pipe->endpoint->edesc->bInterval);
+ usb_callout(sc->sc_poll_handle, sc->sc_interval, slhci_poll_hub, xfer);
+ sc->sc_intr_xfer = xfer;
+ return USBD_IN_PROGRESS;
+}
+
+static void
+slhci_root_intr_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("SLRIabort "));
+}
+
+static void
+slhci_root_intr_close(usbd_pipe_handle pipe)
+{
+ struct slhci_softc *sc = (struct slhci_softc *)pipe->device->bus;
+
+ DPRINTF(D_TRACE, ("SLRIclose "));
+
+ usb_uncallout(sc->sc_poll_handle, slhci_poll_hub, sc->sc_intr_xfer);
+ sc->sc_intr_xfer = NULL;
+}
+
+static void
+slhci_root_intr_done(usbd_xfer_handle xfer)
+{
+ //DPRINTF(D_XFER, ("RIdn "));
+}
+
+static usbd_status
+slhci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status error;
+
+ DPRINTF(D_TRACE, ("C"));
+
+ error = usb_insert_transfer(xfer);
+ if (error)
+ return error;
+
+ return slhci_device_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue));
+}
+
+static usbd_status
+slhci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ usb_device_request_t *req = &xfer->request;
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct slhci_softc *sc = (struct slhci_softc *)pipe->device->bus;
+ usbd_status status = USBD_NORMAL_COMPLETION;
+ u_char *buf;
+ int pid = SL11_PID_OUT;
+ int len, actlen, size;
+ int s;
+ u_int8_t toggle = 0;
+
+ DPRINTF(D_TRACE, ("st "));
+#ifdef SLHCI_DEBUG
+ if ((slhci_debug & D_TRACE))
+ print_req_hub(req);
+#endif
+
+ /* SETUP transaction */
+ if (slhci_transaction(sc, pipe, SL11_PID_SETUP,
+ sizeof(*req), (u_char*)req, toggle) == -1) {
+ status = USBD_IOERROR;
+ goto ret;
+ }
+ toggle ^= SL11_EPCTRL_DATATOGGLE;
+
+ /* DATA transaction */
+ actlen = 0;
+ len = UGETW(req->wLength);
+ if (len) {
+ buf = xfer->buffer;
+ if (req->bmRequestType & UT_READ)
+ pid = SL11_PID_IN;
+ for (; actlen < len; ) {
+ size = min(len - actlen, 8/* Minimum size */);
+ if (slhci_transaction(sc, pipe, pid, size, buf, toggle) == -1)
+ break;
+ toggle ^= SL11_EPCTRL_DATATOGGLE;
+ buf += size;
+ actlen += size;
+ }
+ }
+ xfer->actlen = actlen;
+
+ /* ACK (status) */
+ if (pid == SL11_PID_IN)
+ pid = SL11_PID_OUT;
+ else
+ pid = SL11_PID_IN;
+ if (slhci_transaction(sc, pipe, pid, 0, NULL, toggle) == -1)
+ status = USBD_IOERROR;
+
+ ret:
+ xfer->status = status;
+
+#ifdef SLHCI_DEBUG
+ if((slhci_debug & D_TRACE) && UGETW(req->wLength) > 0){
+ int i;
+ for(i=0; i < UGETW(req->wLength); i++)
+ printf("%02x", ((unsigned char *)xfer->buffer)[i]);
+ printf(" ");
+ }
+#endif
+ s = splusb();
+ usb_transfer_complete(xfer);
+ splx(s);
+ return USBD_IN_PROGRESS;
+}
+
+static void
+slhci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("Cab "));
+ slhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+static void
+slhci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(D_TRACE, ("Ccl "));
+}
+
+static void
+slhci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("Cdn "));
+}
+
+static usbd_status
+slhci_device_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status error;
+
+ DPRINTF(D_TRACE, ("INTRtrans "));
+
+ error = usb_insert_transfer(xfer);
+ if (error)
+ return error;
+
+ return slhci_device_intr_start(STAILQ_FIRST(&xfer->pipe->queue));
+}
+
+static usbd_status
+slhci_device_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct slhci_xfer *sx;
+
+ DPRINTF(D_TRACE, ("INTRstart "));
+
+ sx = malloc(sizeof(*sx), M_USB, M_NOWAIT);
+ if (sx == NULL)
+ goto reterr;
+ memset(sx, 0, sizeof(*sx));
+ sx->sx_xfer = xfer;
+ xfer->hcpriv = sx;
+
+ /* initialize callout */
+ usb_callout_init(sx->sx_callout_t);
+ usb_callout(sx->sx_callout_t,
+ MS_TO_TICKS(pipe->endpoint->edesc->bInterval),
+ slhci_poll_device, sx);
+
+ /* ACK */
+ return USBD_IN_PROGRESS;
+
+ reterr:
+ return USBD_IOERROR;
+}
+
+static void
+slhci_poll_device(void *arg)
+{
+ struct slhci_xfer *sx = (struct slhci_xfer *)arg;
+ usbd_xfer_handle xfer = sx->sx_xfer;
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct slhci_softc *sc = (struct slhci_softc *)pipe->device->bus;
+ void *buf;
+ int pid;
+ int r;
+ int s;
+
+ DPRINTF(D_TRACE, ("pldev"));
+
+ usb_callout(sx->sx_callout_t,
+ MS_TO_TICKS(pipe->endpoint->edesc->bInterval),
+ slhci_poll_device, sx);
+
+ /* interrupt transfer */
+ pid = (UE_GET_DIR(pipe->endpoint->edesc->bEndpointAddress) == UE_DIR_IN)
+ ? SL11_PID_IN : SL11_PID_OUT;
+ buf = xfer->buffer;
+
+ r = slhci_transaction(sc, pipe, pid, xfer->length, buf, 0/*toggle*/);
+ if (r < 0) {
+ DPRINTF(D_MSG, ("%s error", __FUNCTION__));
+ return;
+ }
+ /* no change, return NAK */
+ if (r == 0)
+ return;
+
+ xfer->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ xfer->device->bus->intr_context++;
+ usb_transfer_complete(xfer);
+ xfer->device->bus->intr_context--;
+ splx(s);
+}
+
+static void
+slhci_device_intr_abort(usbd_xfer_handle xfer)
+{
+ struct slhci_xfer *sx;
+
+ DPRINTF(D_TRACE, ("INTRabort "));
+
+ sx = xfer->hcpriv;
+ if (sx) {
+ usb_uncallout(sx->sx_callout_t, slhci_poll_device, sx);
+ free(sx, M_USB);
+ xfer->hcpriv = NULL;
+ } else {
+ printf("%s: sx == NULL!\n", __FUNCTION__);
+ }
+ slhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+static void
+slhci_device_intr_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(D_TRACE, ("INTRclose "));
+}
+
+static void
+slhci_device_intr_done(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("INTRdone "));
+}
+
+static usbd_status
+slhci_device_isoc_transfer(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("S"));
+ return USBD_NORMAL_COMPLETION;
+}
+
+static usbd_status
+slhci_device_isoc_start(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("st "));
+ return USBD_NORMAL_COMPLETION;
+}
+
+static void
+slhci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("Sab "));
+}
+
+static void
+slhci_device_isoc_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(D_TRACE, ("Scl "));
+}
+
+static void
+slhci_device_isoc_done(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("Sdn "));
+}
+
+static usbd_status
+slhci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("B"));
+ return USBD_NORMAL_COMPLETION;
+}
+
+static usbd_status
+slhci_device_bulk_start(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("st "));
+ return USBD_NORMAL_COMPLETION;
+}
+
+static void
+slhci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("Bab "));
+}
+
+static void
+slhci_device_bulk_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(D_TRACE, ("Bcl "));
+}
+
+static void
+slhci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ DPRINTF(D_TRACE, ("Bdn "));
+}
+
+#define DATA0_RD (0x03)
+#define DATA0_WR (0x07)
+#define SLHCI_TIMEOUT (5000)
+
+/*
+ * Do a transaction.
+ * return 1 if ACK, 0 if NAK, -1 if error.
+ */
+static int
+slhci_transaction(struct slhci_softc *sc, usbd_pipe_handle pipe,
+ u_int8_t pid, int len, u_char *buf, u_int8_t toggle)
+{
+#ifdef SLHCI_DEBUG
+ char str[64];
+ int i;
+#endif
+ int timeout;
+ int ls_via_hub = 0;
+ int pl;
+ u_int8_t isr;
+ u_int8_t result = 0;
+ u_int8_t devaddr = pipe->device->address;
+ u_int8_t endpointaddr = pipe->endpoint->edesc->bEndpointAddress;
+ u_int8_t endpoint;
+ u_int8_t cmd = DATA0_RD;
+
+ endpoint = UE_GET_ADDR(endpointaddr);
+ DPRINTF(D_XFER, ("\n(%x,%d%s%d,%d) ",
+ pid, len, (pid == SL11_PID_IN) ? "<-" : "->", devaddr, endpoint));
+
+ /* Set registers */
+ sl11write(sc, SL11_E0ADDR, 0x40);
+ sl11write(sc, SL11_E0LEN, len);
+ sl11write(sc, SL11_E0PID, (pid << 4) + endpoint);
+ sl11write(sc, SL11_E0DEV, devaddr);
+
+ /* Set buffer unless PID_IN */
+ if (pid != SL11_PID_IN) {
+ if (len > 0)
+ sl11write_region(sc, 0x40, buf, len);
+ cmd = DATA0_WR;
+ }
+
+ /* timing ? */
+ pl = (len >> 3) + 3;
+
+ /* Low speed device via HUB */
+ /* XXX does not work... */
+ if ((sc->sc_fullspeed) && pipe->device->speed == USB_SPEED_LOW) {
+ pl = len + 16;
+ cmd |= SL11_EPCTRL_PREAMBLE;
+
+ /*
+ * SL811HS/T rev 1.2 has a bug, when it got PID_IN
+ * from LowSpeed device via HUB.
+ */
+ if (sc->sc_sltype == SLTYPE_SL811HS_R12 && pid == SL11_PID_IN) {
+ ls_via_hub = 1;
+ DPRINTF(D_MSG, ("LSvH "));
+ }
+ }
+
+ /* timing ? */
+ if (sl11read(sc, SL811_CSOF) <= (u_int8_t)pl)
+ cmd |= SL11_EPCTRL_SOF;
+
+ /* Transfer */
+ sl11write(sc, SL11_ISR, 0xff);
+ sl11write(sc, SL11_E0CTRL, cmd | toggle);
+
+ /* Polling */
+ for (timeout = SLHCI_TIMEOUT; timeout; timeout--) {
+ isr = sl11read(sc, SL11_ISR);
+ if ((isr & SL11_ISR_USBA))
+ break;
+ }
+
+ /* Check result status */
+ result = sl11read(sc, SL11_E0STAT);
+ if (!(result & SL11_EPSTAT_NAK) && ls_via_hub) {
+ /* Resend PID_IN within 20usec */
+ sl11write(sc, SL11_ISR, 0xff);
+ sl11write(sc, SL11_E0CTRL, SL11_EPCTRL_ARM);
+ }
+
+ sl11write(sc, SL11_ISR, 0xff);
+
+ DPRINTF(D_XFER, ("t=%d i=%x ", SLHCI_TIMEOUT - timeout, isr));
+#ifdef SLHCI_DEBUG
+ bitmask_snprintf(result,
+ "\20\x8STALL\7NAK\6OV\5SETUP\4DATA1\3TIMEOUT\2ERR\1ACK",
+ str, sizeof(str));
+ DPRINTF(D_XFER, ("STAT=%s ", str));
+#endif
+
+ if ((result & SL11_EPSTAT_ERROR))
+ return -1;
+
+ if ((result & SL11_EPSTAT_NAK))
+ return 0;
+
+ /* Read buffer if PID_IN */
+ if (pid == SL11_PID_IN && len > 0) {
+ sl11read_region(sc, buf, 0x40, len);
+#ifdef SLHCI_DEBUG
+ for (i = 0; i < len; i++)
+ DPRINTF(D_XFER, ("%02X ", buf[i]));
+#endif
+ }
+
+ return 1;
+}
+
+void
+slhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ xfer->status = status;
+ usb_transfer_complete(xfer);
+}
+
+void
+slhci_device_clear_toggle(usbd_pipe_handle pipe)
+{
+ DPRINTF(D_TRACE, ("SLdevice_clear_toggle "));
+}
+
+#ifdef SLHCI_DEBUG
+void
+print_req(usb_device_request_t *r)
+{
+ char *xmes[]={
+ "GETSTAT",
+ "CLRFEAT",
+ "res",
+ "SETFEAT",
+ "res",
+ "SETADDR",
+ "GETDESC",
+ "SETDESC",
+ "GETCONF",
+ "SETCONF",
+ "GETIN/F",
+ "SETIN/F",
+ "SYNC_FR"
+ };
+ int req, type, value, index, len;
+
+ req = r->bRequest;
+ type = r->bmRequestType;
+ value = UGETW(r->wValue);
+ index = UGETW(r->wIndex);
+ len = UGETW(r->wLength);
+
+ printf("%x,%s,v=%d,i=%d,l=%d ",
+ type, xmes[req], value, index, len);
+}
+
+void
+print_req_hub(usb_device_request_t *r)
+{
+ struct {
+ int req;
+ int type;
+ char *str;
+ } conf[] = {
+ { 1, 0x20, "ClrHubFeat" },
+ { 1, 0x23, "ClrPortFeat" },
+ { 2, 0xa3, "GetBusState" },
+ { 6, 0xa0, "GetHubDesc" },
+ { 0, 0xa0, "GetHubStat" },
+ { 0, 0xa3, "GetPortStat" },
+ { 7, 0x20, "SetHubDesc" },
+ { 3, 0x20, "SetHubFeat" },
+ { 3, 0x23, "SetPortFeat" },
+ {-1, 0, NULL},
+ };
+ int i;
+ int value, index, len;
+
+ value = UGETW(r->wValue);
+ index = UGETW(r->wIndex);
+ len = UGETW(r->wLength);
+ for (i = 0; ; i++) {
+ if (conf[i].req == -1 )
+ return print_req(r);
+ if (r->bmRequestType == conf[i].type && r->bRequest == conf[i].req) {
+ printf("%s", conf[i].str);
+ break;
+ }
+ }
+ printf(",v=%d,i=%d,l=%d ", value, index, len);
+}
+
+void
+print_dumpreg(struct slhci_softc *sc)
+{
+ printf("00=%02x,01=%02x,02=%02x,03=%02x,04=%02x,"
+ "08=%02x,09=%02x,0A=%02x,0B=%02x,0C=%02x,",
+ sl11read(sc, 0), sl11read(sc, 1),
+ sl11read(sc, 2), sl11read(sc, 3),
+ sl11read(sc, 4), sl11read(sc, 8),
+ sl11read(sc, 9), sl11read(sc, 10),
+ sl11read(sc, 11), sl11read(sc, 12)
+ );
+ printf("CR1=%02x,IER=%02x,0D=%02x,0E=%02x,0F=%02x ",
+ sl11read(sc, 5), sl11read(sc, 6),
+ sl11read(sc, 13), sl11read(sc, 14), sl11read(sc, 15)
+ );
+}
+
+void
+print_xfer(usbd_xfer_handle xfer)
+{
+ printf("xfer: length=%d, actlen=%d, flags=%x, timeout=%d,",
+ xfer->length, xfer->actlen, xfer->flags, xfer->timeout);
+ printf("request{ ");
+ print_req_hub(&xfer->request);
+ printf("} ");
+}
+#endif /* SLHCI_DEBUG */
diff --git a/sys/legacy/dev/usb/sl811hsreg.h b/sys/legacy/dev/usb/sl811hsreg.h
new file mode 100644
index 0000000..f9c0328
--- /dev/null
+++ b/sys/legacy/dev/usb/sl811hsreg.h
@@ -0,0 +1,126 @@
+/* $NetBSD$ */
+/* $FreeBSD$ */
+
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Tetsuya Isaki.
+ *
+ * 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.
+ */
+
+/*
+ * ScanLogic SL811HS/T USB Host Controller
+ */
+
+#define SL11_IDX_ADDR (0x00)
+#define SL11_IDX_DATA (0x01)
+#define SL11_PORTSIZE (0x02)
+
+#define SL11_E0BASE (0x00) /* Base of Control0 */
+#define SL11_E0CTRL (0x00) /* Host Control Register */
+#define SL11_E0ADDR (0x01) /* Host Base Address */
+#define SL11_E0LEN (0x02) /* Host Base Length */
+#define SL11_E0STAT (0x03) /* USB Status (Read) */
+#define SL11_E0PID SL11_E0STAT /* Host PID, Device Endpoint (Write) */
+#define SL11_E0CONT (0x04) /* Transfer Count (Read) */
+#define SL11_E0DEV SL11_E0CONT /* Host Device Address (Write) */
+
+#define SL11_E1BASE (0x08) /* Base of Control1 */
+#define SL11_E1CTRL (SL11_E1BASE + SL11_E0CTRL)
+#define SL11_E1ADDR (SL11_E1BASE + SL11_E0ADDR)
+#define SL11_E1LEN (SL11_E1BASE + SL11_E0LEN)
+#define SL11_E1STAT (SL11_E1BASE + SL11_E0STAT)
+#define SL11_E1PID (SL11_E1BASE + SL11_E0PID)
+#define SL11_E1CONT (SL11_E1BASE + SL11_E0CONT)
+#define SL11_E1DEV (SL11_E1BASE + SL11_E0DEV)
+
+#define SL11_CTRL (0x05) /* Control Register1 */
+#define SL11_IER (0x06) /* Interrupt Enable Register */
+#define SL11_ISR (0x0d) /* Interrupt Status Register */
+#define SL11_DATA (0x0e) /* SOF Counter Low (Write) */
+#define SL11_REV SL11_DATA /* HW Revision Register (Read) */
+#define SL811_CSOF (0x0f) /* SOF Counter High(R), Control2(W) */
+#define SL11_MEM (0x10) /* Memory Buffer (0x10 - 0xff) */
+
+#define SL11_EPCTRL_ARM (0x01)
+#define SL11_EPCTRL_ENABLE (0x02)
+#define SL11_EPCTRL_DIRECTION (0x04)
+#define SL11_EPCTRL_ISO (0x10)
+#define SL11_EPCTRL_SOF (0x20)
+#define SL11_EPCTRL_DATATOGGLE (0x40)
+#define SL11_EPCTRL_PREAMBLE (0x80)
+
+#define SL11_EPPID_PIDMASK (0xf0)
+#define SL11_EPPID_EPMASK (0x0f)
+
+#define SL11_EPSTAT_ACK (0x01)
+#define SL11_EPSTAT_ERROR (0x02)
+#define SL11_EPSTAT_TIMEOUT (0x04)
+#define SL11_EPSTAT_SEQUENCE (0x08)
+#define SL11_EPSTAT_SETUP (0x10)
+#define SL11_EPSTAT_OVERFLOW (0x20)
+#define SL11_EPSTAT_NAK (0x40)
+#define SL11_EPSTAT_STALL (0x80)
+
+#define SL11_CTRL_ENABLESOF (0x01)
+#define SL11_CTRL_EOF2 (0x04)
+#define SL11_CTRL_RESETENGINE (0x08)
+#define SL11_CTRL_JKSTATE (0x10)
+#define SL11_CTRL_LOWSPEED (0x20)
+#define SL11_CTRL_SUSPEND (0x40)
+
+#define SL11_IER_USBA (0x01) /* USB-A done */
+#define SL11_IER_USBB (0x02) /* USB-B done */
+#define SL11_IER_BABBLE (0x04) /* Babble detection */
+#define SL11_IER_SOFTIMER (0x10) /* 1ms SOF timer */
+#define SL11_IER_INSERT (0x20) /* Slave Insert/Remove detection */
+#define SL11_IER_RESET (0x40) /* USB Reset/Resume */
+
+#define SL11_ISR_USBA (0x01) /* USB-A done */
+#define SL11_ISR_USBB (0x02) /* USB-B done */
+#define SL11_ISR_BABBLE (0x04) /* Babble detection */
+#define SL11_ISR_SOFTIMER (0x10) /* 1ms SOF timer */
+#define SL11_ISR_INSERT (0x20) /* Slave Insert/Remove detection */
+#define SL11_ISR_RESET (0x40) /* USB Reset/Resume */
+#define SL11_ISR_DATA (0x80) /* Value of the Data+ pin */
+
+#define SL11_REV_USBA (0x01) /* USB-A */
+#define SL11_REV_USBB (0x02) /* USB-B */
+#define SL11_REV_REVMASK (0xf0) /* HW Revision */
+#define SL11_REV_REVSL11H (0x00) /* HW is SL11H */
+#define SL11_REV_REVSL811HS (0x10) /* HW is SL811HS */
+
+#define SL811_CSOF_SOFMASK (0x3f) /* SOF High Counter */
+#define SL811_CSOF_POLARITY (0x40) /* Change polarity */
+#define SL811_CSOF_MASTER (0x80) /* Master/Slave selection */
+
diff --git a/sys/legacy/dev/usb/sl811hsvar.h b/sys/legacy/dev/usb/sl811hsvar.h
new file mode 100644
index 0000000..6f5e6d6
--- /dev/null
+++ b/sys/legacy/dev/usb/sl811hsvar.h
@@ -0,0 +1,107 @@
+/* $NetBSD$ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Tetsuya Isaki.
+ *
+ * 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.
+ */
+
+/*
+ * ScanLogic SL811HS/T USB Host Controller
+ */
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+#define delay_ms(X) \
+ pause("slhci", MS_TO_TICKS(X))
+
+#define SL11_PID_OUT (0x1)
+#define SL11_PID_IN (0x9)
+#define SL11_PID_SOF (0x5)
+#define SL11_PID_SETUP (0xd)
+
+struct slhci_xfer {
+ usbd_xfer_handle sx_xfer;
+ usb_callout_t sx_callout_t;
+};
+
+struct slhci_softc {
+ struct usbd_bus sc_bus;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+#ifdef __FreeBSD__
+ void *ih;
+ struct resource *io_res;
+ struct resource *irq_res;
+#endif
+
+ void (*sc_enable_power)(void *, int);
+ void (*sc_enable_intr)(void *, int);
+ void *sc_arg;
+ int sc_powerstat;
+#define POWER_ON (1)
+#define POWER_OFF (0)
+#define INTR_ON (1)
+#define INTR_OFF (0)
+
+ struct device *sc_parent; /* parent device */
+ int sc_sltype; /* revision */
+#define SLTYPE_SL11H (0x00)
+#define SLTYPE_SL811HS (0x01)
+#define SLTYPE_SL811HS_R12 SLTYPE_SL811HS
+#define SLTYPE_SL811HS_R14 (0x02)
+
+ u_int8_t sc_addr; /* device address of root hub */
+ u_int8_t sc_conf;
+ STAILQ_HEAD(, usbd_xfer) sc_free_xfers;
+
+ /* Information for the root hub interrupt pipe */
+ int sc_interval;
+ usbd_xfer_handle sc_intr_xfer;
+ usb_callout_t sc_poll_handle;
+
+ int sc_flags;
+#define SLF_RESET (0x01)
+#define SLF_INSERT (0x02)
+#define SLF_ATTACHED (0x04)
+
+ /* Root HUB specific members */
+ int sc_fullspeed;
+ int sc_connect; /* XXX */
+ int sc_change;
+};
+
+int sl811hs_find(struct slhci_softc *);
+int slhci_attach(struct slhci_softc *);
+int slhci_intr(void *);
diff --git a/sys/legacy/dev/usb/slhci_pccard.c b/sys/legacy/dev/usb/slhci_pccard.c
new file mode 100644
index 0000000..794b81d
--- /dev/null
+++ b/sys/legacy/dev/usb/slhci_pccard.c
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (C) 2005 Takanori Watanabe. All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/pccard/pccard_cis.h>
+#include <dev/pccard/pccardreg.h>
+#include <dev/pccard/pccardvar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_port.h>
+#include <dev/usb/sl811hsvar.h>
+#include "pccarddevs.h"
+
+__FBSDID("$FreeBSD$");
+
+static void slhci_pccard_intr(void *arg);
+
+static const struct pccard_product slhci_pccard_products[] = {
+ PCMCIA_CARD(RATOC, REX_CFU1),
+ {NULL}
+};
+
+static int
+slhci_pccard_probe(device_t dev)
+{
+ const struct pccard_product *pp;
+ u_int32_t fcn = PCCARD_FUNCTION_UNSPEC;
+ int error = 0;
+
+ if ((error = pccard_get_function(dev, &fcn)))
+ return error;
+
+ /* if it says its a disk we should register it */
+ if (fcn == PCCARD_FUNCTION_DISK)
+ return 0;
+
+ /* match other devices here, primarily cdrom/dvd rom */
+ if ((pp = pccard_product_lookup(dev, slhci_pccard_products,
+ sizeof(slhci_pccard_products[0]), NULL))) {
+ if (pp->pp_name)
+ device_set_desc(dev, pp->pp_name);
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+slhci_pccard_detach(device_t dev)
+{
+ struct slhci_softc *sc = device_get_softc(dev);
+ bus_generic_detach(dev);
+
+ if (sc->ih)
+ bus_teardown_intr(dev, sc->irq_res, sc->ih);
+ if (sc->io_res)
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->io_res);
+ if (sc->irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->sc_bus.bdev)
+ device_delete_child(dev, sc->sc_bus.bdev);
+ return 0;
+}
+
+static int
+slhci_pccard_attach(device_t dev)
+{
+ struct slhci_softc *sc = device_get_softc(dev);
+ int error = ENXIO;
+ int rid = 0;
+ if ((sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE)) == NULL) {
+ return ENOMEM;
+ }
+ sc->sc_iot = rman_get_bustag(sc->io_res);
+ sc->sc_ioh = rman_get_bushandle(sc->io_res);
+
+ if (sl811hs_find(sc) == -1) {
+ error = ENXIO;
+ goto out;
+ }
+
+ rid = 0;
+ if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
+ printf("CANNOT ALLOC IRQ\n");
+ error = ENOMEM;
+ goto out;
+ }
+ sc->sc_iot = rman_get_bustag(sc->io_res);
+ sc->sc_ioh = rman_get_bushandle(sc->io_res);
+ sc->sc_bus.bdev = device_add_child(dev, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Could not add USB device\n");
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO, NULL, slhci_pccard_intr, sc, &sc->ih);
+ if (error)
+ goto out;
+#if 1
+
+ if (slhci_attach(sc) == -1) {
+ printf("MI attach NO\n");
+ goto out;
+ }
+
+ error = device_probe_and_attach(sc->sc_bus.bdev);
+
+ if (error) {
+ printf("Probing USB bus %x\n", error);
+ goto out;
+ }
+#endif
+ printf("ATTACHED\n");
+ return 0;
+out:
+ slhci_pccard_detach(dev);
+ return error;
+}
+
+#if 0
+static void
+slhci_pccard_enable_power(void *arg, int mode)
+{
+#if 0
+ struct slhci_softc *sc = arg;
+ u_int8_t r;
+#endif
+}
+
+static void
+slhci_pccard_enable_intr(void *arg, int mode)
+{
+#if 0
+ struct slhci_softc *sc = arg;
+ u_int8_t r;
+#endif
+}
+
+#endif
+static void
+slhci_pccard_intr(void *arg)
+{
+#if 1
+ struct slhci_softc *sc = arg;
+ sc = sc;
+ slhci_intr(sc);
+#endif
+}
+
+static device_method_t slhci_pccard_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, slhci_pccard_probe),
+ DEVMETHOD(device_attach, slhci_pccard_attach),
+ DEVMETHOD(device_detach, slhci_pccard_detach),
+
+ {0, 0}
+};
+
+static driver_t slhci_pccard_driver = {
+ "slhci",
+ slhci_pccard_methods,
+ sizeof(struct slhci_softc),
+};
+
+devclass_t slhci_devclass;
+
+DRIVER_MODULE(slhci, pccard, slhci_pccard_driver, slhci_devclass, 0, 0);
+MODULE_DEPEND(slhci, usb, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/u3g.c b/sys/legacy/dev/usb/u3g.c
new file mode 100644
index 0000000..8c6f67e
--- /dev/null
+++ b/sys/legacy/dev/usb/u3g.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 2008 AnyWi Technologies
+ * Author: Andrea Guzzo <aguzzo@anywi.com>
+ * * based on uark.c 1.1 2006/08/14 08:30:22 jsg *
+ * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk *
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Notes:
+ * - The detour through the tty layer is ridiculously expensive wrt buffering
+ * due to the high speeds.
+ * We should consider adding a simple r/w device which allows attaching of PPP
+ * in a more efficient way.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include <dev/usb/ucomvar.h>
+
+#if __FreeBSD_version >= 800000
+#include "opt_u3g.h"
+#endif
+#include "usbdevs.h"
+
+//#define U3G_DEBUG
+#ifdef U3G_DEBUG
+#define DPRINTF(x...) do { if (u3gdebug) device_printf(sc->sc_dev, ##x); } while (0)
+int u3gdebug = 1;
+#else
+#define DPRINTF(x...) /* nop */
+#endif
+
+#define U3G_MAXPORTS 4
+#define U3G_CONFIG_INDEX 0
+
+struct u3g_softc {
+ struct ucom_softc sc_ucom[U3G_MAXPORTS];
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ u_int8_t sc_speed;
+ u_int8_t sc_flags;
+ u_char sc_numports;
+};
+
+static int u3g_open(void *addr, int portno);
+static void u3g_close(void *addr, int portno);
+
+struct ucom_callback u3g_callback = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ u3g_open,
+ u3g_close,
+ NULL,
+ NULL,
+};
+
+
+struct u3g_speeds_s {
+ u_int32_t ispeed;
+ u_int32_t ospeed;
+};
+
+static const struct u3g_speeds_s u3g_speeds[] = {
+#define U3GSP_GPRS 0
+ {64000, 64000},
+#define U3GSP_EDGE 1
+ {384000, 64000},
+#define U3GSP_CDMA 2
+ {384000, 64000},
+#define U3GSP_UMTS 3
+ {384000, 64000},
+#define U3GSP_HSDPA 4
+ {1200000, 384000},
+#define U3GSP_HSUPA 5
+ {1200000, 384000},
+#define U3GSP_HSPA 6
+ {7200000, 384000},
+};
+
+#define U3GIBUFSIZE 1024
+#define U3GOBUFSIZE 1024
+
+/*
+ * Various supported device vendors/products.
+ */
+struct u3g_dev_type_s {
+ struct usb_devno devno;
+ u_int8_t speed;
+ u_int8_t flags;
+#define U3GFL_NONE 0x00
+#define U3GFL_HUAWEI_INIT 0x01 // Requires init command (Huawei cards)
+#define U3GFL_SCSI_EJECT 0x02 // Requires SCSI eject command (Novatel)
+#define U3GFL_SIERRA_INIT 0x04 // Requires init command (Sierra cards)
+#define U3GFL_CMOTECH_INIT 0x08 // Requires init command (CMOTECH cards)
+#define U3GFL_STUB_WAIT 0x80 // Device reappears after a short delay
+};
+
+// Note: The entries marked with XXX should be checked for the correct speed
+// indication to set the buffer sizes.
+static const struct u3g_dev_type_s u3g_devs[] = {
+ /* OEM: Option */
+ {{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G }, U3GSP_UMTS, U3GFL_NONE },
+ {{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD }, U3GSP_UMTS, U3GFL_NONE },
+ {{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS }, U3GSP_UMTS, U3GFL_NONE },
+ {{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36 }, U3GSP_HSDPA, U3GFL_NONE },
+ {{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA }, U3GSP_HSDPA, U3GFL_NONE },
+ {{ USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G }, U3GSP_UMTS, U3GFL_NONE },
+ /* OEM: Qualcomm, Inc. */
+ {{ USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR }, U3GSP_CDMA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM }, U3GSP_CDMA, U3GFL_SCSI_EJECT },
+ /* OEM: Huawei */
+ {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE }, U3GSP_HSDPA, U3GFL_HUAWEI_INIT },
+ {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }, U3GSP_HSPA, U3GFL_HUAWEI_INIT },
+ /* OEM: Novatel */
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM }, U3GSP_CDMA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620 }, U3GSP_UMTS, U3GFL_SCSI_EJECT }, // XXX
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D }, U3GSP_HSUPA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720 }, U3GSP_UMTS, U3GFL_SCSI_EJECT }, // XXX
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727 }, U3GSP_UMTS, U3GFL_SCSI_EJECT }, // XXX
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740 }, U3GSP_HSDPA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2 }, U3GSP_HSDPA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870 }, U3GSP_UMTS, U3GFL_SCSI_EJECT }, // XXX
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620 }, U3GSP_UMTS, U3GFL_SCSI_EJECT }, // XXX
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640 }, U3GSP_UMTS, U3GFL_SCSI_EJECT }, // XXX
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720 }, U3GSP_UMTS, U3GFL_SCSI_EJECT }, // XXX
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740 }, U3GSP_HSDPA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D }, U3GSP_HSUPA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870 }, U3GSP_HSDPA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD }, U3GSP_HSUPA, U3GFL_SCSI_EJECT },
+ {{ USB_VENDOR_DELL, USB_PRODUCT_DELL_U740 }, U3GSP_HSDPA, U3GFL_SCSI_EJECT },
+ /* OEM: Merlin */
+ {{ USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ /* OEM: Sierra Wireless: */
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2 }, U3GSP_HSDPA, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781 }, U3GSP_UMTS, U3GFL_NONE }, // XXX
+ {{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL }, U3GSP_UMTS, U3GFL_SIERRA_INIT },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_HS2300 }, U3GSP_HSDPA, U3GFL_NONE },
+ /* OEM: CMOTECH */
+ {{ USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CGU628 }, U3GSP_HSDPA, U3GFL_CMOTECH_INIT },
+ {{ USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_DISK }, U3GSP_HSDPA, U3GFL_NONE },
+};
+#define u3g_lookup(v, p) ((const struct u3g_dev_type_s *)usb_lookup(u3g_devs, v, p))
+
+static int
+u3g_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ const struct u3g_dev_type_s *u3g_dev_type;
+
+ if (!uaa->iface)
+ return UMATCH_NONE;
+
+ u3g_dev_type = u3g_lookup(uaa->vendor, uaa->product);
+ if (!u3g_dev_type)
+ return UMATCH_NONE;
+
+ if (u3g_dev_type->flags&U3GFL_HUAWEI_INIT) {
+ /* If the interface class of the first interface is no longer
+ * mass storage the card has changed to modem (see u3g_attach()
+ * below).
+ */
+ usb_interface_descriptor_t *id;
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (!id || id->bInterfaceClass == UICLASS_MASS)
+ return UMATCH_NONE;
+ }
+
+ return UMATCH_VENDOR_PRODUCT_CONF_IFACE;
+}
+
+static int
+u3g_attach(device_t self)
+{
+ struct u3g_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ const struct u3g_dev_type_s *u3g_dev_type;
+ usbd_device_handle dev = uaa->device;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i, n;
+ usb_config_descriptor_t *cd;
+ char devnamefmt[32];
+
+#if __FreeBSD_version < 700000
+ char *devinfo = malloc(1024, M_USBDEV, M_WAITOK);
+ usbd_devinfo(dev, 0, devinfo);
+ device_printf(self, "%s\n", devinfo);
+ free(devinfo, M_USBDEV);
+#endif
+
+ /* get the config descriptor */
+ cd = usbd_get_config_descriptor(dev);
+ if (cd == NULL) {
+ device_printf(self, "failed to get configuration descriptor\n");
+ return ENXIO;
+ }
+
+ sc->sc_dev = self;
+ sc->sc_udev = dev;
+
+ u3g_dev_type = u3g_lookup(uaa->vendor, uaa->product);
+ sc->sc_flags = u3g_dev_type->flags;
+ sc->sc_speed = u3g_dev_type->speed;
+
+ sprintf(devnamefmt,"U%d.%%d", device_get_unit(self));
+ int portno = 0;
+ for (i = 0; i < uaa->nifaces && portno < U3G_MAXPORTS; i++) {
+ if (uaa->ifaces[i] == NULL)
+ continue;
+
+ id = usbd_get_interface_descriptor(uaa->ifaces[i]);
+ if (id && id->bInterfaceClass == UICLASS_MASS) {
+ /* We attach to the interface instead of the device as
+ * some devices have a built-in SD card reader.
+ * Claim the first umass device (cdX) as it contains
+ * only Windows drivers anyway (CD-ROM), hiding it.
+ */
+#ifndef U3G_DEBUG
+ if (!bootverbose)
+ if (uaa->vendor == USB_VENDOR_HUAWEI)
+ if (id->bInterfaceNumber == 2)
+ uaa->ifaces[i] = NULL;
+#endif
+ continue;
+ }
+
+ int bulkin_no = -1, bulkout_no = -1;
+ int claim_iface = 0;
+ for (n = 0; n < id->bNumEndpoints && portno < U3G_MAXPORTS; n++) {
+ ed = usbd_interface2endpoint_descriptor(uaa->ifaces[i], n);
+ if (ed == NULL)
+ continue;
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ bulkin_no = ed->bEndpointAddress;
+ else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT
+ && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ bulkout_no = ed->bEndpointAddress;
+
+ /* If we have found a pair of bulk-in/-out endpoints
+ * create a serial port for it. Note: We assume that
+ * the bulk-in and bulk-out endpoints appear in pairs.
+ */
+ if (bulkin_no != -1 && bulkout_no != -1) {
+ struct ucom_softc *ucom = &sc->sc_ucom[portno];
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->ifaces[i];
+ ucom->sc_bulkin_no = bulkin_no;
+ ucom->sc_bulkout_no = bulkout_no;
+ ucom->sc_ibufsize = U3GIBUFSIZE;
+ ucom->sc_ibufsizepad = U3GIBUFSIZE;
+ ucom->sc_obufsize = U3GOBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+
+ ucom->sc_callback = &u3g_callback;
+ ucom->sc_parent = sc;
+ ucom->sc_portno = portno;
+
+ DPRINTF("port=%d iface=%d in=0x%x out=0x%x\n",
+ portno, i,
+ ucom->sc_bulkin_no,
+ ucom->sc_bulkout_no);
+#if __FreeBSD_version < 700000
+ ucom_attach_tty(ucom, MINOR_CALLOUT, devnamefmt, portno);
+#elif __FreeBSD_version < 800000
+ ucom_attach_tty(ucom, TS_CALLOUT, devnamefmt, portno);
+#else
+ ucom_attach_tty(ucom, devnamefmt, portno);
+#endif
+
+ claim_iface = 1;
+ portno++;
+ bulkin_no = bulkout_no = -1;
+ }
+ }
+ if (claim_iface)
+ uaa->ifaces[i] = NULL; // claim the interface
+ }
+ sc->sc_numports = portno;
+
+ device_printf(self, "configured %d serial ports (%s)\n",
+ sc->sc_numports, devnamefmt);
+ return 0;
+}
+
+static int
+u3g_detach(device_t self)
+{
+ struct u3g_softc *sc = device_get_softc(self);
+ int rv = 0;
+ int i;
+
+ for (i = 0; i < sc->sc_numports; i++) {
+ sc->sc_ucom[i].sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom[i]);
+ if (rv != 0) {
+ device_printf(self, "ucom_detach(U%d.%d\n", device_get_unit(self), i);
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+static int
+u3g_open(void *addr, int portno)
+{
+#if __FreeBSD_version < 800000
+ /* Supply generous buffering for these cards to avoid disappointments
+ * when setting the speed incorrectly. Only do this for the first port
+ * assuming that the rest of the ports are used for diagnostics only
+ * anyway.
+ * Note: We abuse the fact that ucom sets the speed through
+ * ispeed/ospeed, not through ispeedwat/ospeedwat.
+ */
+ if (portno == 0) {
+ struct u3g_softc *sc = addr;
+ struct ucom_softc *ucom = &sc->sc_ucom[portno];
+ struct tty *tp = ucom->sc_tty;
+
+ tp->t_ispeedwat = u3g_speeds[sc->sc_speed].ispeed;
+ tp->t_ospeedwat = u3g_speeds[sc->sc_speed].ospeed;
+
+ /* Avoid excessive buffer sizes.
+ * XXX The values here should be checked. Lower them and see
+ * whether 'lost chars' messages appear.
+ */
+ if (tp->t_ispeedwat > 384000)
+ tp->t_ispeedwat = 384000;
+ if (tp->t_ospeedwat > 384000)
+ tp->t_ospeedwat = 384000;
+
+ ttsetwater(tp);
+ }
+#endif
+
+ return 0;
+}
+
+static void
+u3g_close(void *addr, int portno)
+{
+#if __FreeBSD_version < 800000
+ if (portno == 0) { /* see u3g_open() */
+ /* Reduce the buffers allocated above again */
+ struct u3g_softc *sc = addr;
+ struct ucom_softc *ucom = &sc->sc_ucom[portno];
+ struct tty *tp = ucom->sc_tty;
+#ifdef U3G_DEBUG
+ device_t self = sc->sc_dev;
+#endif
+
+ tp->t_ispeedwat = (speed_t)-1;
+ tp->t_ospeedwat = (speed_t)-1;
+
+ ttsetwater(tp);
+ }
+#endif
+}
+
+static device_method_t u3g_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, u3g_match),
+ DEVMETHOD(device_attach, u3g_attach),
+ DEVMETHOD(device_detach, u3g_detach),
+
+ { 0, 0 }
+};
+
+static driver_t u3g_driver = {
+ "ucom",
+ u3g_methods,
+ sizeof (struct u3g_softc)
+};
+
+DRIVER_MODULE(u3g, uhub, u3g_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(u3g, usb, 1, 1, 1);
+MODULE_DEPEND(u3g, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(u3g, 1);
+
+/*******************************************************************
+ ****** Stub driver to hide devices that need to reinitialise ******
+ *******************************************************************/
+
+struct u3gstub_softc {
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_pipe_handle sc_pipe_out, sc_pipe_in;
+ usbd_xfer_handle sc_xfer;
+};
+
+static int
+u3gstub_huawei_init(struct u3gstub_softc *sc, struct usb_attach_arg *uaa)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
+ USETW(req.wIndex, UHF_PORT_SUSPEND);
+ USETW(req.wLength, 0);
+
+ (void) usbd_do_request(uaa->device, &req, 0); /* ignore any error */
+
+ return 1;
+}
+
+static void
+u3gstub_BBB_cb(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err)
+{
+ struct u3gstub_softc *sc = (struct u3gstub_softc *) priv;
+ unsigned char cmd[13];
+
+ if (err) {
+ device_printf(sc->sc_dev,
+ "Failed to send CD eject command to "
+ "change to modem mode\n");
+ } else {
+ usbd_setup_xfer(sc->sc_xfer, sc->sc_pipe_in, NULL, cmd, sizeof(cmd),
+ 0, USBD_DEFAULT_TIMEOUT, NULL);
+ int err = usbd_transfer(sc->sc_xfer) != USBD_NORMAL_COMPLETION;
+ if (err != USBD_NORMAL_COMPLETION && err != USBD_IN_PROGRESS)
+ DPRINTF("failed to start transfer (CSW)\n");
+ }
+}
+
+static int
+u3gstub_scsi_eject(struct u3gstub_softc *sc, struct usb_attach_arg *uaa)
+{
+ /* See definition of umass_bbb_cbw_t in sys/dev/usb/umass.c and struct
+ * scsi_start_stop_unit in sys/cam/scsi/scsi_all.h .
+ */
+ unsigned char cmd[31] = {
+ 0x55, 0x53, 0x42, 0x43, /* 0..3: Command Block Wrapper (CBW) signature */
+ 0x01, 0x00, 0x00, 0x00, /* 4..7: CBW Tag, unique 32-bit number */
+ 0x00, 0x00, 0x00, 0x00, /* 8..11: CBW Transfer Length, no data here */
+ 0x00, /* 12: CBW Flag: output */
+ 0x00, /* 13: CBW Lun */
+ 0x06, /* 14: CBW Length */
+
+ 0x1b, /* 15+0: opcode: SCSI START/STOP */
+ 0x00, /* 15+1: byte2: Not immediate */
+ 0x00, 0x00, /* 15+2..3: reserved */
+ 0x02, /* 15+4: Load/Eject command */
+ 0x00, /* 15+5: control */
+ 0x00, 0x00, 0x00, 0x00, /* 15+6..15: unused */
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed = NULL;
+ int i;
+
+
+ /* Find the bulk-out endpoints */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ for (i = 0 ; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
+ if (ed != NULL
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) {
+ if (usbd_open_pipe(uaa->iface,
+ ed->bEndpointAddress,
+ USBD_EXCLUSIVE_USE,
+ &sc->sc_pipe_out)
+ != USBD_NORMAL_COMPLETION) {
+ DPRINTF("failed to open bulk-out pipe on endpoint %d\n",
+ ed->bEndpointAddress);
+ return 0;
+ }
+ } else {
+ if (usbd_open_pipe(uaa->iface,
+ ed->bEndpointAddress,
+ USBD_EXCLUSIVE_USE,
+ &sc->sc_pipe_in)
+ != USBD_NORMAL_COMPLETION) {
+ DPRINTF("failed to open bulk-in pipe on endpoint %d\n",
+ ed->bEndpointAddress);
+ return 0;
+ }
+ }
+ }
+ if (sc->sc_pipe_out && sc->sc_pipe_in)
+ break;
+ }
+
+ if (i == id->bNumEndpoints) {
+ DPRINTF("failed to find bulk-out pipe\n");
+ return 0;
+ }
+
+ sc->sc_xfer = usbd_alloc_xfer(uaa->device);
+ if (sc->sc_xfer == NULL) {
+ DPRINTF("failed to allocate xfer\n");
+ return 0;
+ }
+
+ usbd_setup_xfer(sc->sc_xfer, sc->sc_pipe_out, NULL, cmd, sizeof(cmd),
+ 0, USBD_DEFAULT_TIMEOUT, u3gstub_BBB_cb);
+ int err = usbd_transfer(sc->sc_xfer) != USBD_NORMAL_COMPLETION;
+ if (err != USBD_NORMAL_COMPLETION && err != USBD_IN_PROGRESS) {
+ DPRINTF("failed to start transfer (CBW)\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+u3gstub_cmotech_init(struct u3gstub_softc *sc, struct usb_attach_arg *uaa)
+{
+ /* See definition of umass_bbb_cbw_t in sys/dev/usb/umass.c
+ * in sys/cam/scsi/scsi_all.h .
+ */
+ unsigned char cmd[31] = {
+ 0x55, 0x53, 0x42, 0x43, /* 0..3: Command Block Wrapper (CBW) signature */
+ 0x01, 0x00, 0x00, 0x00, /* 4..7: CBW Tag, unique 32-bit number */
+ 0x00, 0x00, 0x00, 0x00, /* 8..11: CBW Transfer Length, no data here */
+ 0x80, /* 12: CBW Flag: output, so 0 */
+ 0x00, /* 13: CBW Lun */
+ 0x08, /* 14: CBW Length */
+
+ 0xff, /* 15+0 */
+ 0x52, /* 15+1 */
+ 0x44, /* 15+2 */
+ 0x45, /* 15+2 */
+ 0x56, /* 15+4 */
+ 0x43, /* 15+5 */
+ 0x48, /* 15+5 */
+ 0x47, /* 15+5 */
+ 0x00, 0x00, 0x00, 0x00, /* 15+8..15: unused */
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed = NULL;
+ int i;
+
+
+ /* Find the bulk-out endpoints */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ for (i = 0 ; i < id->bNumEndpoints ; i++) {
+ ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
+ if (ed != NULL
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) {
+ if (usbd_open_pipe(uaa->iface,
+ ed->bEndpointAddress,
+ USBD_EXCLUSIVE_USE,
+ &sc->sc_pipe_out)
+ != USBD_NORMAL_COMPLETION) {
+ DPRINTF("failed to open bulk-out pipe on endpoint %d\n",
+ ed->bEndpointAddress);
+ return 0;
+ }
+ } else {
+ if (usbd_open_pipe(uaa->iface,
+ ed->bEndpointAddress,
+ USBD_EXCLUSIVE_USE,
+ &sc->sc_pipe_in)
+ != USBD_NORMAL_COMPLETION) {
+ DPRINTF("failed to open bulk-in pipe on endpoint %d\n",
+ ed->bEndpointAddress);
+ return 0;
+ }
+ }
+ }
+ if (sc->sc_pipe_out && sc->sc_pipe_in)
+ break;
+ }
+
+ if (i == id->bNumEndpoints) {
+ DPRINTF("failed to find bulk-out pipe\n");
+ return 0;
+ }
+
+ sc->sc_xfer = usbd_alloc_xfer(uaa->device);
+ if (sc->sc_xfer == NULL) {
+ DPRINTF("failed to allocate xfer\n");
+ return 0;
+ }
+
+ usbd_setup_xfer(sc->sc_xfer, sc->sc_pipe_out, NULL, cmd, sizeof(cmd),
+ 0, USBD_DEFAULT_TIMEOUT, u3gstub_BBB_cb);
+ int err = usbd_transfer(sc->sc_xfer) != USBD_NORMAL_COMPLETION;
+ if (err != USBD_NORMAL_COMPLETION && err != USBD_IN_PROGRESS) {
+ DPRINTF("failed to start transfer (CBW)\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+u3gstub_sierra_init(struct u3gstub_softc *sc, struct usb_attach_arg *uaa)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_VENDOR;
+ req.bRequest = UR_SET_INTERFACE;
+ USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
+ USETW(req.wIndex, UHF_PORT_CONNECTION);
+ USETW(req.wLength, 0);
+
+ (void) usbd_do_request(uaa->device, &req, 0); /* ignore any error */
+
+ return 1;
+}
+
+static int
+u3gstub_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ const struct u3g_dev_type_s *u3g_dev_type;
+ usb_interface_descriptor_t *id;
+
+ /* This stub handles 3G modem devices (E220, Mobile, etc.) with
+ * auto-install flash disks for Windows/MacOSX on the first interface.
+ * After some command or some delay they change appearance to a modem.
+ */
+
+ if (!uaa->iface)
+ return UMATCH_NONE;
+
+ u3g_dev_type = u3g_lookup(uaa->vendor, uaa->product);
+ if (!u3g_dev_type)
+ return UMATCH_NONE;
+
+ if (u3g_dev_type->flags&U3GFL_HUAWEI_INIT
+ || u3g_dev_type->flags&U3GFL_SCSI_EJECT
+ || u3g_dev_type->flags&U3GFL_SIERRA_INIT
+ || u3g_dev_type->flags&U3GFL_CMOTECH_INIT
+ || u3g_dev_type->flags&U3GFL_STUB_WAIT) {
+ /* We assume that if the first interface is still a mass
+ * storage device the device has not yet changed appearance.
+ */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id && id->bInterfaceNumber == 0
+ && id->bInterfaceClass == UICLASS_MASS) {
+#ifndef U3G_DEBUG
+ if (!bootverbose)
+ device_quiet(self);
+#endif
+
+ return UMATCH_VENDOR_PRODUCT;
+ }
+ }
+
+ return UMATCH_NONE;
+}
+
+static int
+u3gstub_attach(device_t self)
+{
+ struct u3gstub_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ const struct u3g_dev_type_s *u3g_dev_type;
+ int i;
+
+#ifndef U3G_DEBUG
+ if (!bootverbose)
+ device_quiet(self);
+#endif
+
+ sc->sc_dev = self;
+ sc->sc_udev = uaa->device;
+
+ for (i = 0; i < uaa->nifaces; i++)
+ uaa->ifaces[i] = NULL; // claim all interfaces
+
+ u3g_dev_type = u3g_lookup(uaa->vendor, uaa->product);
+ if (u3g_dev_type->flags&U3GFL_HUAWEI_INIT) {
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "changing Huawei modem to modem mode\n");
+ if (!u3gstub_huawei_init(sc, uaa))
+ return ENXIO;
+ } else if (u3g_dev_type->flags&U3GFL_SCSI_EJECT) {
+ if (bootverbose)
+ device_printf(sc->sc_dev, "sending CD eject command to "
+ "change to modem mode\n");
+ if (!u3gstub_scsi_eject(sc, uaa))
+ return ENXIO;
+ } else if (u3g_dev_type->flags&U3GFL_SIERRA_INIT) {
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "changing Sierra modem to modem mode\n");
+ if (!u3gstub_sierra_init(sc, uaa))
+ return ENXIO;
+ } else if (u3g_dev_type->flags&U3GFL_CMOTECH_INIT) {
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "changing CMOTECH modem to modem mode\n");
+ if (!u3gstub_cmotech_init(sc, uaa))
+ return ENXIO;
+ } else if (u3g_dev_type->flags&U3GFL_STUB_WAIT) {
+ if (bootverbose)
+ device_printf(sc->sc_dev, "waiting for modem to change "
+ "to modem mode\n");
+ /* nop */
+ }
+
+ return 0;
+}
+
+static int
+u3gstub_detach(device_t self)
+{
+ struct u3gstub_softc *sc = device_get_softc(self);
+
+ if (sc->sc_xfer)
+ usbd_free_xfer(sc->sc_xfer);
+
+ if (sc->sc_pipe_in) {
+ usbd_abort_pipe(sc->sc_pipe_in);
+ usbd_close_pipe(sc->sc_pipe_in);
+ }
+ if (sc->sc_pipe_out) {
+ usbd_abort_pipe(sc->sc_pipe_out);
+ usbd_close_pipe(sc->sc_pipe_out);
+ }
+
+ return 0;
+}
+
+static device_method_t u3gstub_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, u3gstub_match),
+ DEVMETHOD(device_attach, u3gstub_attach),
+ DEVMETHOD(device_detach, u3gstub_detach),
+
+ { 0, 0 }
+};
+
+static driver_t u3gstub_driver = {
+ "u3g",
+ u3gstub_methods,
+ sizeof (struct u3gstub_softc)
+};
+
+DRIVER_MODULE(u3gstub, uhub, u3gstub_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(u3gstub, usb, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/uark.c b/sys/legacy/dev/usb/uark.c
new file mode 100644
index 0000000..af85819
--- /dev/null
+++ b/sys/legacy/dev/usb/uark.c
@@ -0,0 +1,339 @@
+/* $OpenBSD: uark.c,v 1.1 2006/08/14 08:30:22 jsg Exp $ */
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef UARK_DEBUG
+#define DPRINTFN(n, x) do { \
+ if (uarkdebug > (n)) \
+ printf x; \
+} while (0)
+int uarkebug = 0;
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UARKBUFSZ 256
+#define UARK_CONFIG_NO 0
+#define UARK_IFACE_NO 0
+
+#define UARK_SET_DATA_BITS(x) (x - 5)
+
+#define UARK_PARITY_NONE 0x00
+#define UARK_PARITY_ODD 0x08
+#define UARK_PARITY_EVEN 0x18
+
+#define UARK_STOP_BITS_1 0x00
+#define UARK_STOP_BITS_2 0x04
+
+#define UARK_BAUD_REF 3000000
+
+#define UARK_WRITE 0x40
+#define UARK_READ 0xc0
+
+#define UARK_REQUEST 0xfe
+
+#define UARK_CONFIG_INDEX 0
+#define UARK_IFACE_INDEX 0
+
+struct uark_softc {
+ struct ucom_softc sc_ucom;
+ usbd_interface_handle sc_iface;
+
+ u_char sc_msr;
+ u_char sc_lsr;
+};
+
+static void uark_get_status(void *, int portno, u_char *lsr, u_char *msr);
+static void uark_set(void *, int, int, int);
+static int uark_param(void *, int, struct termios *);
+static void uark_break(void *, int, int);
+static int uark_cmd(struct uark_softc *, uint16_t, uint16_t);
+
+struct ucom_callback uark_callback = {
+ uark_get_status,
+ uark_set,
+ uark_param,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static const struct usb_devno uark_devs[] = {
+ { USB_VENDOR_ARKMICRO, USB_PRODUCT_ARKMICRO_ARK3116 }
+};
+
+static int
+uark_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ return (usb_lookup(uark_devs, uaa->vendor, uaa->product) != NULL) ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+}
+
+static int
+uark_attach(device_t self)
+{
+ struct uark_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status error;
+ int i;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+
+ if (uaa->iface == NULL) {
+ /* Move the device into the configured state. */
+ error = usbd_set_config_index(dev, UARK_CONFIG_INDEX, 1);
+ if (error) {
+ device_printf(ucom->sc_dev,
+ "failed to set configuration, err=%s\n",
+ usbd_errstr(error));
+ goto bad;
+ }
+ error =
+ usbd_device2interface_handle(dev, UARK_IFACE_INDEX, &iface);
+ if (error) {
+ device_printf(ucom->sc_dev,
+ "failed to get interface, err=%s\n",
+ usbd_errstr(error));
+ goto bad;
+ }
+ } else
+ iface = uaa->iface;
+
+ id = usbd_get_interface_descriptor(iface);
+ ucom->sc_iface = iface;
+
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ device_printf(ucom->sc_dev,
+ "could not read endpoint descriptor\n");
+ goto bad;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ if (ucom->sc_bulkin_no == -1 || ucom->sc_bulkout_no == -1) {
+ device_printf(ucom->sc_dev, "missing endpoint\n");
+ goto bad;
+ }
+ ucom->sc_parent = sc;
+ ucom->sc_ibufsize = UARKBUFSZ;
+ ucom->sc_obufsize = UARKBUFSZ;
+ ucom->sc_ibufsizepad = UARKBUFSZ;
+ ucom->sc_opkthdrlen = 0;
+
+ ucom->sc_callback = &uark_callback;
+
+ DPRINTF(("uark: in=0x%x out=0x%x\n", ucom->sc_bulkin_no, ucom->sc_bulkout_no));
+ ucom_attach(&sc->sc_ucom);
+ return 0;
+
+bad:
+ DPRINTF(("uark_attach: ATTACH ERROR\n"));
+ ucom->sc_dying = 1;
+ return ENXIO;
+}
+
+static int
+uark_detach(device_t self)
+{
+ struct uark_softc *sc = device_get_softc(self);
+ int rv = 0;
+
+ DPRINTF(("uark_detach: sc=%p\n", sc));
+ sc->sc_ucom.sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
+
+static void
+uark_set(void *vsc, int portno, int reg, int onoff)
+{
+ struct uark_softc *sc = vsc;
+
+ switch (reg) {
+ case UCOM_SET_BREAK:
+ uark_break(sc, portno, onoff);
+ return;
+ /* NOTREACHED */
+ case UCOM_SET_DTR:
+ case UCOM_SET_RTS:
+ default:
+ return;
+ /* NOTREACHED */
+ }
+}
+
+static int
+uark_param(void *vsc, int portno, struct termios *t)
+{
+ struct uark_softc *sc = (struct uark_softc *)vsc;
+ int data, divisor;
+ speed_t speed = t->c_ospeed;
+
+ if (speed < 300 || speed > 115200)
+ return (EINVAL);
+ divisor = (UARK_BAUD_REF + speed / 2) / speed;
+ /* Check that we're within 3% of the requested rate. */
+ if (speed * divisor < UARK_BAUD_REF * 100 / 103 ||
+ speed * divisor > UARK_BAUD_REF * 100 / 97)
+ return (EINVAL);
+ uark_cmd(sc, 3, 0x83);
+ uark_cmd(sc, 0, divisor & 0xFF);
+ uark_cmd(sc, 1, divisor >> 8);
+ uark_cmd(sc, 3, 0x03);
+
+ if (ISSET(t->c_cflag, CSTOPB))
+ data = UARK_STOP_BITS_2;
+ else
+ data = UARK_STOP_BITS_1;
+
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ data |= UARK_PARITY_ODD;
+ else
+ data |= UARK_PARITY_EVEN;
+ } else
+ data |= UARK_PARITY_NONE;
+
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ data |= UARK_SET_DATA_BITS(5);
+ break;
+ case CS6:
+ data |= UARK_SET_DATA_BITS(6);
+ break;
+ case CS7:
+ data |= UARK_SET_DATA_BITS(7);
+ break;
+ case CS8:
+ data |= UARK_SET_DATA_BITS(8);
+ break;
+ }
+ uark_cmd(sc, 3, 0x00);
+ uark_cmd(sc, 3, data);
+
+ return (0);
+}
+
+void
+uark_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
+{
+ struct uark_softc *sc = vsc;
+
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+}
+
+void
+uark_break(void *vsc, int portno, int onoff)
+{
+#ifdef UARK_DEBUG
+ struct uark_softc *sc = vsc;
+
+ device_printf(sc->sc_dev, "%s: break %s!\n", onoff ? "on" : "off");
+ if (onoff)
+ /* break on */
+ uark_cmd(sc, 4, 0x01);
+ else
+ uark_cmd(sc, 4, 0x00);
+#endif
+}
+
+int
+uark_cmd(struct uark_softc *sc, uint16_t index, uint16_t value)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+
+ req.bmRequestType = UARK_WRITE;
+ req.bRequest = UARK_REQUEST;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+
+ if (err)
+ return (EIO);
+
+ return (0);
+}
+
+static device_method_t uark_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uark_match),
+ DEVMETHOD(device_attach, uark_attach),
+ DEVMETHOD(device_detach, uark_detach),
+
+ { 0, 0 }
+};
+
+static driver_t uark_driver = {
+ "ucom",
+ uark_methods,
+ sizeof (struct uark_softc)
+};
+
+DRIVER_MODULE(uark, uhub, uark_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uark, usb, 1, 1, 1);
+MODULE_DEPEND(uark, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
diff --git a/sys/legacy/dev/usb/ubsa.c b/sys/legacy/dev/usb/ubsa.c
new file mode 100644
index 0000000..b66cce8
--- /dev/null
+++ b/sys/legacy/dev/usb/ubsa.c
@@ -0,0 +1,742 @@
+/*-
+ * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/taskqueue.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef USB_DEBUG
+static int ubsadebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa");
+SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RW,
+ &ubsadebug, 0, "ubsa debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (ubsadebug > (n)) \
+ printf x; \
+ } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UBSA_MODVER 1 /* module version */
+
+#define UBSA_CONFIG_INDEX 1
+#define UBSA_IFACE_INDEX 0
+
+#define UBSA_INTR_INTERVAL 100 /* ms */
+
+#define UBSA_SET_BAUDRATE 0x00
+#define UBSA_SET_STOP_BITS 0x01
+#define UBSA_SET_DATA_BITS 0x02
+#define UBSA_SET_PARITY 0x03
+#define UBSA_SET_DTR 0x0A
+#define UBSA_SET_RTS 0x0B
+#define UBSA_SET_BREAK 0x0C
+#define UBSA_SET_FLOW_CTRL 0x10
+
+#define UBSA_PARITY_NONE 0x00
+#define UBSA_PARITY_EVEN 0x01
+#define UBSA_PARITY_ODD 0x02
+#define UBSA_PARITY_MARK 0x03
+#define UBSA_PARITY_SPACE 0x04
+
+#define UBSA_FLOW_NONE 0x0000
+#define UBSA_FLOW_OCTS 0x0001
+#define UBSA_FLOW_ODSR 0x0002
+#define UBSA_FLOW_IDSR 0x0004
+#define UBSA_FLOW_IDTR 0x0008
+#define UBSA_FLOW_IRTS 0x0010
+#define UBSA_FLOW_ORTS 0x0020
+#define UBSA_FLOW_UNKNOWN 0x0040
+#define UBSA_FLOW_OXON 0x0080
+#define UBSA_FLOW_IXON 0x0100
+
+/* line status register */
+#define UBSA_LSR_TSRE 0x40 /* Transmitter empty: byte sent */
+#define UBSA_LSR_TXRDY 0x20 /* Transmitter buffer empty */
+#define UBSA_LSR_BI 0x10 /* Break detected */
+#define UBSA_LSR_FE 0x08 /* Framing error: bad stop bit */
+#define UBSA_LSR_PE 0x04 /* Parity error */
+#define UBSA_LSR_OE 0x02 /* Overrun, lost incoming byte */
+#define UBSA_LSR_RXRDY 0x01 /* Byte ready in Receive Buffer */
+#define UBSA_LSR_RCV_MASK 0x1f /* Mask for incoming data or error */
+
+/* modem status register */
+/* All deltas are from the last read of the MSR. */
+#define UBSA_MSR_DCD 0x80 /* Current Data Carrier Detect */
+#define UBSA_MSR_RI 0x40 /* Current Ring Indicator */
+#define UBSA_MSR_DSR 0x20 /* Current Data Set Ready */
+#define UBSA_MSR_CTS 0x10 /* Current Clear to Send */
+#define UBSA_MSR_DDCD 0x08 /* DCD has changed state */
+#define UBSA_MSR_TERI 0x04 /* RI has toggled low to high */
+#define UBSA_MSR_DDSR 0x02 /* DSR has changed state */
+#define UBSA_MSR_DCTS 0x01 /* CTS has changed state */
+
+struct ubsa_softc {
+ struct ucom_softc sc_ucom;
+
+ int sc_iface_number; /* interface number */
+
+ usbd_interface_handle sc_intr_iface; /* interrupt interface */
+ int sc_intr_number; /* interrupt number */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
+ u_char *sc_intr_buf; /* interrupt buffer */
+ int sc_isize;
+
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* ubsa status register */
+ struct task sc_task;
+};
+
+static void ubsa_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void ubsa_notify(void *, int count);
+
+static void ubsa_get_status(void *, int, u_char *, u_char *);
+static void ubsa_set(void *, int, int, int);
+static int ubsa_param(void *, int, struct termios *);
+static int ubsa_open(void *, int);
+static void ubsa_close(void *, int);
+
+static int ubsa_request(struct ubsa_softc *, u_int8_t, u_int16_t);
+static void ubsa_dtr(struct ubsa_softc *, int);
+static void ubsa_rts(struct ubsa_softc *, int);
+static void ubsa_baudrate(struct ubsa_softc *, speed_t);
+static void ubsa_parity(struct ubsa_softc *, tcflag_t);
+static void ubsa_databits(struct ubsa_softc *, tcflag_t);
+static void ubsa_stopbits(struct ubsa_softc *, tcflag_t);
+static void ubsa_flow(struct ubsa_softc *, tcflag_t, tcflag_t);
+
+struct ucom_callback ubsa_callback = {
+ ubsa_get_status,
+ ubsa_set,
+ ubsa_param,
+ NULL,
+ ubsa_open,
+ ubsa_close,
+ NULL,
+ NULL
+};
+
+static const struct ubsa_product {
+ uint16_t vendor;
+ uint16_t product;
+} ubsa_products [] = {
+ /* AnyData ADU-500A */
+ { USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_500A },
+ /* AnyData ADU-E100A/H */
+ { USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_E100X },
+ /* Axesstel MV100H */
+ { USB_VENDOR_AXESSTEL, USB_PRODUCT_AXESSTEL_DATAMODEM },
+ /* BELKIN F5U103 */
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103 },
+ /* BELKIN F5U120 */
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120 },
+ /* GoHubs GO-COM232 */
+ { USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM },
+ /* GoHubs GO-COM232 */
+ { USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232 },
+ /* Peracom */
+ { USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1 },
+ { 0, 0 }
+};
+
+static device_probe_t ubsa_match;
+static device_attach_t ubsa_attach;
+static device_detach_t ubsa_detach;
+
+static device_method_t ubsa_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ubsa_match),
+ DEVMETHOD(device_attach, ubsa_attach),
+ DEVMETHOD(device_detach, ubsa_detach),
+ { 0, 0 }
+};
+
+static driver_t ubsa_driver = {
+ "ucom",
+ ubsa_methods,
+ sizeof (struct ubsa_softc)
+};
+
+DRIVER_MODULE(ubsa, uhub, ubsa_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(ubsa, usb, 1, 1, 1);
+MODULE_DEPEND(ubsa, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(ubsa, UBSA_MODVER);
+
+static int
+ubsa_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ int i;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ for (i = 0; ubsa_products[i].vendor != 0; i++) {
+ if (ubsa_products[i].vendor == uaa->vendor &&
+ ubsa_products[i].product == uaa->product) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ }
+ return (UMATCH_NONE);
+}
+
+static int
+ubsa_attach(device_t self)
+{
+ struct ubsa_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status err;
+ int i;
+
+ dev = uaa->device;
+ ucom = &sc->sc_ucom;
+
+ /*
+ * initialize rts, dtr variables to something
+ * different from boolean 0, 1
+ */
+ sc->sc_dtr = -1;
+ sc->sc_rts = -1;
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ DPRINTF(("ubsa attach: sc = %p\n", sc));
+
+ /* initialize endpoints */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UBSA_CONFIG_INDEX, 1);
+ if (err) {
+ device_printf(ucom->sc_dev, "failed to set configuration: %s\n",
+ usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the config descriptor */
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+
+ if (cdesc == NULL) {
+ device_printf(ucom->sc_dev,
+ "failed to get configuration descriptor\n");
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the first interface */
+ err = usbd_device2interface_handle(dev, UBSA_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ device_printf(ucom->sc_dev, "failed to get interface: %s\n",
+ usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* Find the endpoints */
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(ucom->sc_dev,
+ "no endpoint descriptor for %d\n", i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ ucom->sc_ibufsize = UGETW(ed->wMaxPacketSize);
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ ucom->sc_obufsize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ if (sc->sc_intr_number == -1) {
+ device_printf(ucom->sc_dev, "Could not find interrupt in\n");
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* keep interface for interrupt */
+ sc->sc_intr_iface = ucom->sc_iface;
+
+ if (ucom->sc_bulkin_no == -1) {
+ device_printf(ucom->sc_dev, "Could not find data bulk in\n");
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (ucom->sc_bulkout_no == -1) {
+ device_printf(ucom->sc_dev, "Could not find data bulk out\n");
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = 1024;
+ ucom->sc_obufsize = 1024;
+ ucom->sc_ibufsizepad = ucom->sc_ibufsize;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &ubsa_callback;
+
+ DPRINTF(("ubsa: in = 0x%x, out = 0x%x, intr = 0x%x\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no, sc->sc_intr_number));
+
+ TASK_INIT(&sc->sc_task, 0, ubsa_notify, sc);
+ ucom_attach(ucom);
+ return 0;
+
+error:
+ return ENXIO;
+}
+
+static int
+ubsa_detach(device_t self)
+{
+ struct ubsa_softc *sc = device_get_softc(self);
+ int rv;
+
+ DPRINTF(("ubsa_detach: sc = %p\n", sc));
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+#if 0
+ taskqueue_drain(taskqueue_swi_giant);
+#endif
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
+
+static int
+ubsa_request(struct ubsa_softc *sc, u_int8_t request, u_int16_t value)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = request;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err)
+ device_printf(sc->sc_ucom.sc_dev, "ubsa_request(%x, %x): %s\n",
+ request, value, usbd_errstr(err));
+ return (err);
+}
+
+static void
+ubsa_dtr(struct ubsa_softc *sc, int onoff)
+{
+
+ DPRINTF(("ubsa_dtr: onoff = %d\n", onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+ sc->sc_dtr = onoff;
+
+ ubsa_request(sc, UBSA_SET_DTR, onoff ? 1 : 0);
+}
+
+static void
+ubsa_rts(struct ubsa_softc *sc, int onoff)
+{
+
+ DPRINTF(("ubsa_rts: onoff = %d\n", onoff));
+
+ if (sc->sc_rts == onoff)
+ return;
+ sc->sc_rts = onoff;
+
+ ubsa_request(sc, UBSA_SET_RTS, onoff ? 1 : 0);
+}
+
+static void
+ubsa_break(struct ubsa_softc *sc, int onoff)
+{
+
+ DPRINTF(("ubsa_rts: onoff = %d\n", onoff));
+
+ ubsa_request(sc, UBSA_SET_BREAK, onoff ? 1 : 0);
+}
+
+static void
+ubsa_set(void *addr, int portno, int reg, int onoff)
+{
+ struct ubsa_softc *sc;
+
+ sc = addr;
+ switch (reg) {
+ case UCOM_SET_DTR:
+ ubsa_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ ubsa_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ ubsa_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ubsa_baudrate(struct ubsa_softc *sc, speed_t speed)
+{
+ u_int16_t value = 0;
+
+ DPRINTF(("ubsa_baudrate: speed = %d\n", speed));
+
+ switch(speed) {
+ case B0:
+ break;
+ case B300:
+ case B600:
+ case B1200:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ case B230400:
+ value = B230400 / speed;
+ break;
+ default:
+ device_printf(sc->sc_ucom.sc_dev,
+ "ubsa_param: unsupported baud, forcing default of 9600\n");
+ value = B230400 / B9600;
+ break;
+ };
+
+ if (speed == B0) {
+ ubsa_flow(sc, 0, 0);
+ ubsa_dtr(sc, 0);
+ ubsa_rts(sc, 0);
+ } else
+ ubsa_request(sc, UBSA_SET_BAUDRATE, value);
+}
+
+static void
+ubsa_parity(struct ubsa_softc *sc, tcflag_t cflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_parity: cflag = 0x%x\n", cflag));
+
+ if (cflag & PARENB)
+ value = (cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN;
+ else
+ value = UBSA_PARITY_NONE;
+
+ ubsa_request(sc, UBSA_SET_PARITY, value);
+}
+
+static void
+ubsa_databits(struct ubsa_softc *sc, tcflag_t cflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_databits: cflag = 0x%x\n", cflag));
+
+ switch (cflag & CSIZE) {
+ case CS5: value = 0; break;
+ case CS6: value = 1; break;
+ case CS7: value = 2; break;
+ case CS8: value = 3; break;
+ default:
+ device_printf(sc->sc_ucom.sc_dev,
+ "ubsa_param: unsupported databits requested, "
+ "forcing default of 8\n");
+ value = 3;
+ }
+
+ ubsa_request(sc, UBSA_SET_DATA_BITS, value);
+}
+
+static void
+ubsa_stopbits(struct ubsa_softc *sc, tcflag_t cflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_stopbits: cflag = 0x%x\n", cflag));
+
+ value = (cflag & CSTOPB) ? 1 : 0;
+
+ ubsa_request(sc, UBSA_SET_STOP_BITS, value);
+}
+
+static void
+ubsa_flow(struct ubsa_softc *sc, tcflag_t cflag, tcflag_t iflag)
+{
+ int value;
+
+ DPRINTF(("ubsa_flow: cflag = 0x%x, iflag = 0x%x\n", cflag, iflag));
+
+ value = 0;
+ if (cflag & CRTSCTS)
+ value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS;
+ if (iflag & (IXON|IXOFF))
+ value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON;
+
+ ubsa_request(sc, UBSA_SET_FLOW_CTRL, value);
+}
+
+static int
+ubsa_param(void *addr, int portno, struct termios *ti)
+{
+ struct ubsa_softc *sc;
+
+ sc = addr;
+
+ DPRINTF(("ubsa_param: sc = %p\n", sc));
+
+ ubsa_baudrate(sc, ti->c_ospeed);
+ ubsa_parity(sc, ti->c_cflag);
+ ubsa_databits(sc, ti->c_cflag);
+ ubsa_stopbits(sc, ti->c_cflag);
+ ubsa_flow(sc, ti->c_cflag, ti->c_iflag);
+
+ return (0);
+}
+
+static int
+ubsa_open(void *addr, int portno)
+{
+ struct ubsa_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying)
+ return (ENXIO);
+
+ DPRINTF(("ubsa_open: sc = %p\n", sc));
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_intr_iface,
+ sc->sc_intr_number,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intr_pipe,
+ sc,
+ sc->sc_intr_buf,
+ sc->sc_isize,
+ ubsa_intr,
+ UBSA_INTR_INTERVAL);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev,
+ "cannot open interrupt pipe (addr %d)\n",
+ sc->sc_intr_number);
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+static void
+ubsa_close(void *addr, int portno)
+{
+ struct ubsa_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ DPRINTF(("ubsa_close: close\n"));
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ device_printf(sc->sc_ucom.sc_dev,
+ "abort interrupt pipe failed: %s\n",
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ device_printf(sc->sc_ucom.sc_dev,
+ "close interrupt pipe failed: %s\n",
+ usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
+
+static void
+ubsa_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ubsa_softc *sc;
+ u_char *buf;
+
+ sc = priv;
+ buf = sc->sc_intr_buf;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ DPRINTF(("%s: ubsa_intr: abnormal status: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev),
+ usbd_errstr(status)));
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ /* incidentally, Belkin adapter status bits match UART 16550 bits */
+ sc->sc_lsr = buf[2];
+ sc->sc_msr = buf[3];
+
+ DPRINTF(("%s: ubsa lsr = 0x%02x, msr = 0x%02x\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), sc->sc_lsr, sc->sc_msr));
+
+ taskqueue_enqueue(taskqueue_swi_giant, &sc->sc_task);
+}
+
+/* Handle delayed events. */
+static void
+ubsa_notify(void *arg, int count)
+{
+ struct ubsa_softc *sc;
+
+ sc = arg;
+ ucom_status_change(&sc->sc_ucom);
+}
+
+static void
+ubsa_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct ubsa_softc *sc;
+
+ DPRINTF(("ubsa_get_status\n"));
+
+ sc = addr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
diff --git a/sys/legacy/dev/usb/ubser.c b/sys/legacy/dev/usb/ubser.c
new file mode 100644
index 0000000..29994b9
--- /dev/null
+++ b/sys/legacy/dev/usb/ubser.c
@@ -0,0 +1,882 @@
+/*-
+ * Copyright (c) 2004 Bernd Walter <ticso@freebsd.org>
+ *
+ * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $
+ * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
+ * $Author: ticso $
+ * $Rev: 1127 $
+ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BWCT serial adapter driver
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/serial.h>
+#include <sys/tty.h>
+#include <sys/clist.h>
+#include <sys/file.h>
+
+#include <sys/selinfo.h>
+
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/ubser.h>
+
+#ifdef USB_DEBUG
+static int ubserdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser");
+SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RW,
+ &ubserdebug, 0, "ubser debug level");
+#define DPRINTF(x) do { \
+ if (ubserdebug) \
+ printf x; \
+ } while (0)
+
+#define DPRINTFN(n, x) do { \
+ if (ubserdebug > (n)) \
+ printf x; \
+ } while (0)
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define ISSET(t, f) ((t) & (f))
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~((unsigned)(f))
+
+struct ubser_port {
+ int p_port;
+ struct ubser_softc *p_sc;
+ usbd_xfer_handle p_oxfer; /* write request */
+ u_char *p_obuf; /* write buffer */
+ struct tty *p_tty;
+};
+
+struct ubser_softc {
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface; /* data interface */
+ int sc_ifaceno;
+
+ int sc_refcnt;
+ u_char sc_dying;
+ u_char sc_opening;
+ int sc_state;
+ uint8_t sc_numser;
+
+ int sc_bulkin_no; /* bulk in endpoint address */
+ usbd_pipe_handle sc_bulkin_pipe; /* bulk in pipe */
+ usbd_xfer_handle sc_ixfer; /* read request */
+ u_char *sc_ibuf; /* read buffer */
+ u_int sc_ibufsize; /* read buffer size */
+ u_int sc_ibufsizepad; /* read buffer size padded */
+
+ int sc_bulkout_no; /* bulk out endpoint address */
+ usbd_pipe_handle sc_bulkout_pipe;/* bulk out pipe */
+ u_int sc_obufsize; /* write buffer size */
+ u_int sc_opkthdrlen; /* header length of
+ output packet */
+
+ struct ubser_port *sc_port;
+};
+
+static int ubserparam(struct tty *, struct termios *);
+static void ubserstart(struct tty *);
+static void ubserstop(struct tty *, int);
+static usbd_status ubserstartread(struct ubser_softc *);
+static void ubserreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void ubserwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void ubser_cleanup(struct ubser_softc *sc);
+
+static t_break_t ubserbreak;
+static t_open_t ubseropen;
+static t_close_t ubserclose;
+static t_modem_t ubsermodem;
+
+static device_probe_t ubser_match;
+static device_attach_t ubser_attach;
+static device_detach_t ubser_detach;
+
+static device_method_t ubser_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ubser_match),
+ DEVMETHOD(device_attach, ubser_attach),
+ DEVMETHOD(device_detach, ubser_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ubser_driver = {
+ "ubser",
+ ubser_methods,
+ sizeof(struct ubser_softc)
+};
+
+static devclass_t ubser_devclass;
+
+static int
+ubser_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_string_descriptor_t us;
+ usb_interface_descriptor_t *id;
+ usb_device_descriptor_t *dd;
+ int err, size;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ DPRINTFN(20,("ubser: vendor=0x%x, product=0x%x\n",
+ uaa->vendor, uaa->product));
+
+ dd = usbd_get_device_descriptor(uaa->device);
+ if (dd == NULL) {
+ printf("ubser: failed to get device descriptor\n");
+ return (UMATCH_NONE);
+ }
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id == NULL) {
+ printf("ubser: failed to get interface descriptor\n");
+ return (UMATCH_NONE);
+ }
+
+ err = usbd_get_string_desc(uaa->device, dd->iManufacturer, 0, &us,
+ &size);
+ if (err != 0)
+ return (UMATCH_NONE);
+
+ /* check if this is a BWCT vendor specific ubser interface */
+ if (strcmp((char*)us.bString, "B\0W\0C\0T\0") == 0 &&
+ id->bInterfaceClass == 0xff && id->bInterfaceSubClass == 0x00)
+ return (UMATCH_VENDOR_IFACESUBCLASS);
+
+ return (UMATCH_NONE);
+}
+
+static int
+ubser_attach(device_t self)
+{
+ struct ubser_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle udev = uaa->device;
+ usb_endpoint_descriptor_t *ed;
+ usb_interface_descriptor_t *id;
+ usb_device_request_t req;
+ struct tty *tp;
+ usbd_status err;
+ int i;
+ int alen;
+ uint8_t epcount;
+ struct ubser_port *pp;
+
+ sc->sc_dev = self;
+
+ DPRINTFN(10,("\nubser_attach: sc=%p\n", sc));
+
+ sc->sc_udev = udev = uaa->device;
+ sc->sc_iface = uaa->iface;
+ sc->sc_numser = 0;
+ sc->sc_port = NULL;
+
+ /* get interface index */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id == NULL) {
+ printf("ubser: failed to get interface descriptor\n");
+ return (UMATCH_NONE);
+ }
+ sc->sc_ifaceno = id->bInterfaceNumber;
+
+ /* get number of serials */
+ req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+ req.bRequest = VENDOR_GET_NUMSER;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 1);
+ err = usbd_do_request_flags(udev, &req, &sc->sc_numser,
+ USBD_SHORT_XFER_OK, &alen, USBD_DEFAULT_TIMEOUT);
+ if (err) {
+ device_printf(self, "failed to get number of serials\n");
+ goto bad;
+ } else if (alen != 1) {
+ device_printf(self, "bogus answer on get_numser\n");
+ goto bad;
+ }
+ if (sc->sc_numser > MAX_SER)
+ sc->sc_numser = MAX_SER;
+ device_printf(self, "found %i serials\n", sc->sc_numser);
+
+ sc->sc_port = malloc(sizeof(*sc->sc_port) * sc->sc_numser,
+ M_USBDEV, M_WAITOK);
+
+ /* find our bulk endpoints */
+ epcount = 0;
+ (void)usbd_endpoint_count(sc->sc_iface, &epcount);
+ sc->sc_bulkin_no = -1;
+ sc->sc_bulkout_no = -1;
+ for (i = 0; i < epcount; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(self, "couldn't get ep %d\n", i);
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_bulkin_no = ed->bEndpointAddress;
+ sc->sc_ibufsizepad = UGETW(ed->wMaxPacketSize);
+ sc->sc_ibufsizepad = UGETW(ed->wMaxPacketSize) - 1;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_bulkout_no = ed->bEndpointAddress;
+ sc->sc_obufsize = UGETW(ed->wMaxPacketSize) - 1;
+ sc->sc_opkthdrlen = 1;
+ }
+ }
+ if (sc->sc_bulkin_no == -1 || sc->sc_bulkout_no == -1) {
+ device_printf(self, "could not find bulk in/out endpoint\n");
+ sc->sc_dying = 1;
+ goto bad;
+ }
+
+ /* Open the bulk pipes */
+ /* Bulk-in pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
+ &sc->sc_bulkin_pipe);
+ if (err) {
+ device_printf(self, "open bulk in error (addr %d): %s\n",
+ sc->sc_bulkin_no, usbd_errstr(err));
+ goto fail_0;
+ }
+ /* Bulk-out pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ device_printf(self, "open bulk out error (addr %d): %s\n",
+ sc->sc_bulkout_no, usbd_errstr(err));
+ goto fail_1;
+ }
+
+ /* Allocate a request and an input buffer */
+ sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_ixfer == NULL) {
+ goto fail_2;
+ }
+
+ sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
+ sc->sc_ibufsizepad);
+ if (sc->sc_ibuf == NULL) {
+ goto fail_3;
+ }
+
+ for (i = 0; i < sc->sc_numser; i++) {
+ pp = &sc->sc_port[i];
+ pp->p_port = i;
+ pp->p_sc = sc;
+ tp = pp->p_tty = ttyalloc();
+ tp->t_sc = pp;
+ DPRINTF(("ubser_attach: tty_attach tp = %p\n", tp));
+ tp->t_oproc = ubserstart;
+ tp->t_param = ubserparam;
+ tp->t_stop = ubserstop;
+ tp->t_break = ubserbreak;
+ tp->t_open = ubseropen;
+ tp->t_close = ubserclose;
+ tp->t_modem = ubsermodem;
+ ttycreate(tp, 0, "y%r%r", device_get_unit(sc->sc_dev), i);
+ }
+
+
+ for (i = 0; i < sc->sc_numser; i++) {
+ sc->sc_port[i].p_oxfer = NULL;
+ sc->sc_port[i].p_obuf = NULL;
+ }
+ for (i = 0; i < sc->sc_numser; i++) {
+ sc->sc_port[i].p_oxfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_port[i].p_oxfer == NULL) {
+ goto fail_4;
+ }
+
+ sc->sc_port[i].p_obuf = usbd_alloc_buffer(sc->sc_port[i].p_oxfer,
+ sc->sc_obufsize +
+ sc->sc_opkthdrlen);
+ if (sc->sc_port[i].p_obuf == NULL) {
+ goto fail_4;
+ }
+ }
+
+ ubserstartread(sc);
+ return 0;
+
+fail_4:
+ for (i = 0; i < sc->sc_numser; i++) {
+ if (sc->sc_port[i].p_oxfer != NULL) {
+ usbd_free_xfer(sc->sc_port[i].p_oxfer);
+ sc->sc_port[i].p_oxfer = NULL;
+ }
+ }
+fail_3:
+ usbd_free_xfer(sc->sc_ixfer);
+ sc->sc_ixfer = NULL;
+fail_2:
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+fail_1:
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+fail_0:
+ sc->sc_opening = 0;
+ wakeup(&sc->sc_opening);
+
+bad:
+ ubser_cleanup(sc);
+ if (sc->sc_port != NULL) {
+ for (i = 0; i < sc->sc_numser; i++) {
+ pp = &sc->sc_port[i];
+ if (pp->p_tty != NULL)
+ ttyfree(pp->p_tty);
+ }
+ free(sc->sc_port, M_USBDEV);
+ sc->sc_port = NULL;
+ }
+
+ DPRINTF(("ubser_attach: ATTACH ERROR\n"));
+ return ENXIO;
+}
+
+static int
+ubser_detach(device_t self)
+{
+ struct ubser_softc *sc = device_get_softc(self);
+ int i;
+ struct ubser_port *pp;
+
+ DPRINTF(("ubser_detach: sc=%p\n", sc));
+
+ sc->sc_dying = 1;
+ for (i = 0; i < sc->sc_numser; i++) {
+ pp = &sc->sc_port[i];
+ if (pp->p_tty != NULL)
+ ttygone(pp->p_tty);
+ }
+
+ if (sc->sc_bulkin_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (sc->sc_bulkout_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+
+ if (sc->sc_port != NULL) {
+ for (i = 0; i < sc->sc_numser; i++) {
+ pp = &sc->sc_port[i];
+ if (pp->p_tty != NULL)
+ ttyfree(pp->p_tty);
+ }
+ free(sc->sc_port, M_USBDEV);
+ sc->sc_port = NULL;
+ }
+
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(sc->sc_dev);
+ }
+
+ return (0);
+}
+
+static int
+ubserparam(struct tty *tp, struct termios *t)
+{
+ struct ubser_softc *sc;
+ struct ubser_port *pp;
+
+ pp = tp->t_sc;
+ sc = pp->p_sc;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("ubserparam: sc = %p\n", sc));
+
+ /*
+ * The firmware on our devices can only do 8n1@9600bps
+ * without handshake.
+ * We refuse to accept other configurations.
+ */
+
+ /* enshure 9600bps */
+ switch (t->c_ospeed) {
+ case 9600:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* 2 stop bits not possible */
+ if (ISSET(t->c_cflag, CSTOPB))
+ return (EINVAL);
+
+ /* XXX parity handling not possible with current firmware */
+ if (ISSET(t->c_cflag, PARENB))
+ return (EINVAL);
+
+ /* we can only do 8 data bits */
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS8:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* we can't do any kind of hardware handshaking */
+ if ((t->c_cflag &
+ (CRTS_IFLOW | CDTR_IFLOW |CDSR_OFLOW |CCAR_OFLOW)) != 0)
+ return (EINVAL);
+
+ /*
+ * XXX xon/xoff not supported by the firmware!
+ * This is handled within FreeBSD only and may overflow buffers
+ * because of delayed reaction due to device buffering.
+ */
+
+ ttsetwater(tp);
+
+ return (0);
+}
+
+static void
+ubserstart(struct tty *tp)
+{
+ struct ubser_softc *sc;
+ struct ubser_port *pp;
+ struct cblock *cbp;
+ usbd_status err;
+ u_char *data;
+ int cnt;
+ uint8_t serial;
+
+ pp = tp->t_sc;
+ sc = pp->p_sc;
+ serial = pp->p_port;
+ DPRINTF(("ubserstart: sc = %p, tp = %p\n", sc, tp));
+
+ if (sc->sc_dying)
+ return;
+
+ if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
+ ttwwakeup(tp);
+ DPRINTF(("ubserstart: stopped\n"));
+ return;
+ }
+
+ if (tp->t_outq.c_cc <= tp->t_olowat) {
+ if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
+ CLR(tp->t_state, TS_SO_OLOWAT);
+ wakeup(TSA_OLOWAT(tp));
+ }
+ selwakeuppri(&tp->t_wsel, TTIPRI);
+ if (tp->t_outq.c_cc == 0) {
+ if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
+ TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
+ CLR(tp->t_state, TS_SO_OCOMPLETE);
+ wakeup(TSA_OCOMPLETE(tp));
+ }
+ return;
+ }
+ }
+
+ /* Grab the first contiguous region of buffer space. */
+ data = tp->t_outq.c_cf;
+ cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
+ cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
+
+ if (cnt == 0) {
+ DPRINTF(("ubserstart: cnt == 0\n"));
+ return;
+ }
+
+ SET(tp->t_state, TS_BUSY);
+
+ if (cnt + sc->sc_opkthdrlen > sc->sc_obufsize) {
+ DPRINTF(("ubserstart: big buffer %d chars\n", cnt));
+ cnt = sc->sc_obufsize;
+ }
+ sc->sc_port[serial].p_obuf[0] = serial;
+ memcpy(sc->sc_port[serial].p_obuf + sc->sc_opkthdrlen, data, cnt);
+
+
+ DPRINTF(("ubserstart: %d chars\n", cnt));
+ usbd_setup_xfer(sc->sc_port[serial].p_oxfer, sc->sc_bulkout_pipe,
+ (usbd_private_handle)tp, sc->sc_port[serial].p_obuf,
+ cnt + sc->sc_opkthdrlen,
+ USBD_NO_COPY, USBD_NO_TIMEOUT, ubserwritecb);
+ /* What can we do on error? */
+ err = usbd_transfer(sc->sc_port[serial].p_oxfer);
+ if (err != USBD_IN_PROGRESS)
+ printf("ubserstart: err=%s\n", usbd_errstr(err));
+
+ ttwwakeup(tp);
+}
+
+static void
+ubserstop(struct tty *tp, int flag)
+{
+ struct ubser_softc *sc;
+
+ sc = tp->t_sc;
+
+ DPRINTF(("ubserstop: %d\n", flag));
+
+ if (flag & FWRITE) {
+ DPRINTF(("ubserstop: write\n"));
+ if (ISSET(tp->t_state, TS_BUSY)) {
+ /* XXX do what? */
+ if (!ISSET(tp->t_state, TS_TTSTOP))
+ SET(tp->t_state, TS_FLUSH);
+ }
+ }
+
+ DPRINTF(("ubserstop: done\n"));
+}
+
+static void
+ubserwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct tty *tp;
+ struct ubser_softc *sc;
+ struct ubser_port *pp;
+ u_int32_t cc;
+
+ tp = (struct tty *)p;
+ pp = tp->t_sc;
+ sc = pp->p_sc;
+
+ DPRINTF(("ubserwritecb: status = %d\n", status));
+
+ if (status == USBD_CANCELLED || sc->sc_dying)
+ goto error;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ device_printf(sc->sc_dev, "ubserwritecb: %s\n",
+ usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ /* XXX we should restart after some delay. */
+ goto error;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
+ DPRINTF(("ubserwritecb: cc = %d\n", cc));
+ if (cc <= sc->sc_opkthdrlen) {
+ device_printf(sc->sc_dev, "sent size too small, cc = %d\n",
+ cc);
+ goto error;
+ }
+
+ /* convert from USB bytes to tty bytes */
+ cc -= sc->sc_opkthdrlen;
+
+ CLR(tp->t_state, TS_BUSY);
+ if (ISSET(tp->t_state, TS_FLUSH))
+ CLR(tp->t_state, TS_FLUSH);
+ else
+ ndflush(&tp->t_outq, cc);
+ ttyld_start(tp);
+
+ return;
+
+error:
+ CLR(tp->t_state, TS_BUSY);
+ return;
+}
+
+static usbd_status
+ubserstartread(struct ubser_softc *sc)
+{
+ usbd_status err;
+
+ DPRINTF(("ubserstartread: start\n"));
+
+ if (sc->sc_bulkin_pipe == NULL)
+ return (USBD_NORMAL_COMPLETION);
+
+ usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
+ (usbd_private_handle)sc,
+ sc->sc_ibuf, sc->sc_ibufsizepad,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, ubserreadcb);
+
+ err = usbd_transfer(sc->sc_ixfer);
+ if (err != USBD_IN_PROGRESS) {
+ DPRINTF(("ubserstartread: err = %s\n", usbd_errstr(err)));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static void
+ubserreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct ubser_softc *sc = (struct ubser_softc *)p;
+ struct tty *tp;
+ usbd_status err;
+ u_int32_t cc;
+ u_char *cp;
+ int lostcc;
+
+ if (status == USBD_IOERROR) {
+ device_printf(sc->sc_dev, "ubserreadcb: %s - restarting\n",
+ usbd_errstr(status));
+ goto resubmit;
+ }
+
+ DPRINTF(("ubserreadcb: status = %d\n", status));
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status != USBD_CANCELLED) {
+ device_printf(sc->sc_dev, "ubserreadcb: %s\n",
+ usbd_errstr(status));
+ }
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
+
+ DPRINTF(("ubserreadcb: got %d bytes from device\n", cc));
+ if (cc == 0)
+ goto resubmit;
+
+ if (cc > sc->sc_ibufsizepad) {
+ device_printf(sc->sc_dev, "invalid receive data size, %d chars\n",
+ cc);
+ goto resubmit;
+ }
+
+ /* parse header */
+ if (cc < 1)
+ goto resubmit;
+ DPRINTF(("ubserreadcb: got %d chars for serial %d\n", cc - 1, *cp));
+ tp = sc->sc_port[*cp].p_tty;
+ cp++;
+ cc--;
+
+ if (cc < 1)
+ goto resubmit;
+
+ if (!(tp->t_state & TS_ISOPEN)) /* drop data for unused serials */
+ goto resubmit;
+
+ if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
+ if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
+ && (tp->t_iflag & IXOFF)
+ && !(tp->t_state & TS_TBLOCK))
+ ttyblock(tp);
+ lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
+ tp->t_rawcc += cc;
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ ubserstart(tp);
+ }
+ if (lostcc > 0)
+ device_printf(sc->sc_dev, "lost %d chars\n", lostcc);
+ } else {
+ /* Give characters to tty layer. */
+ while (cc > 0) {
+ DPRINTFN(7, ("ubserreadcb: char = 0x%02x\n", *cp));
+ if (ttyld_rint(tp, *cp) == -1) {
+ /* XXX what should we do? */
+ device_printf(sc->sc_dev, "lost %d chars\n",
+ cc);
+ break;
+ }
+ cc--;
+ cp++;
+ }
+ }
+
+resubmit:
+ err = ubserstartread(sc);
+ if (err) {
+ device_printf(sc->sc_dev, "read start failed\n");
+ /* XXX what should we do now? */
+ }
+
+}
+
+static void
+ubser_cleanup(struct ubser_softc *sc)
+{
+ int i;
+ struct ubser_port *pp;
+
+ DPRINTF(("ubser_cleanup: closing pipes\n"));
+
+ if (sc->sc_bulkin_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+ }
+ if (sc->sc_bulkout_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+ }
+ if (sc->sc_ixfer != NULL) {
+ usbd_free_xfer(sc->sc_ixfer);
+ sc->sc_ixfer = NULL;
+ }
+ for (i = 0; i < sc->sc_numser; i++) {
+ pp = &sc->sc_port[i];
+ if (pp->p_oxfer != NULL) {
+ usbd_free_xfer(pp->p_oxfer);
+ pp->p_oxfer = NULL;
+ }
+ }
+}
+
+static int
+ubseropen(struct tty *tp, struct cdev *dev)
+{
+ struct ubser_softc *sc;
+ struct ubser_port *pp;
+
+ pp = tp->t_sc;
+ sc = pp->p_sc;
+
+ sc->sc_refcnt++; /* XXX: wrong refcnt on error later on */
+ return (0);
+}
+
+static void
+ubserclose(struct tty *tp)
+{
+ struct ubser_softc *sc;
+ struct ubser_port *pp;
+
+ pp = tp->t_sc;
+ sc = pp->p_sc;
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+}
+
+static void
+ubserbreak(struct tty *tp, int sig)
+{
+ usb_device_request_t req;
+ struct ubser_softc *sc;
+ struct ubser_port *pp;
+ int error;
+ int alen;
+
+ pp = tp->t_sc;
+ sc = pp->p_sc;
+ if (sig) {
+ DPRINTF(("ubser_break: TIOCSBRK\n"));
+ req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+ req.bRequest = VENDOR_SET_BREAK;
+ USETW(req.wValue, pp->p_port);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 0);
+ error = usbd_do_request_flags(sc->sc_udev, &req, &sc->sc_numser,
+ USBD_SHORT_XFER_OK, &alen, USBD_DEFAULT_TIMEOUT);
+ }
+}
+
+static int
+ubsermodem(struct tty *tp, int sigon, int sigoff)
+{
+
+ return (SER_DTR | SER_RTS | SER_DCD);
+}
+
+MODULE_DEPEND(ubser, usb, 1, 1, 1);
+DRIVER_MODULE(ubser, uhub, ubser_driver, ubser_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/ubser.h b/sys/legacy/dev/usb/ubser.h
new file mode 100644
index 0000000..f256d4a
--- /dev/null
+++ b/sys/legacy/dev/usb/ubser.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2003 Bernd Walter <ticso@freebsd.org>
+ *
+ * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.h $
+ * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
+ * $Author: ticso $
+ * $Rev: 1127 $
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef UBSER_H
+#define UBSER_H
+
+#define MAX_SER 32
+
+/* Vendor Interface Requests */
+#define VENDOR_GET_NUMSER 0x01
+#define VENDOR_SET_BREAK 0x02
+#define VENDOR_CLEAR_BREAK 0x03
+
+#endif /* UBSER_H */
diff --git a/sys/legacy/dev/usb/uchcom.c b/sys/legacy/dev/usb/uchcom.c
new file mode 100644
index 0000000..85b2629
--- /dev/null
+++ b/sys/legacy/dev/usb/uchcom.c
@@ -0,0 +1,1036 @@
+/* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */
+
+/*-
+ * Copyright (c) 2007, Takanori Watanabe
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Takuya SHIOZAKI (tshiozak@netbsd.org).
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * driver for WinChipHead CH341/340, the worst USB-serial chip in the world.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+#include "usbdevs.h"
+
+#ifdef UCHCOM_DEBUG
+#define DPRINTFN(n, x) if (uchcomdebug > (n)) logprintf x
+int uchcomdebug = 0;
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UCHCOM_IFACE_INDEX 0
+#define UCHCOM_CONFIG_INDEX 0
+
+#define UCHCOM_REV_CH340 0x0250
+#define UCHCOM_INPUT_BUF_SIZE 8
+
+#define UCHCOM_REQ_GET_VERSION 0x5F
+#define UCHCOM_REQ_READ_REG 0x95
+#define UCHCOM_REQ_WRITE_REG 0x9A
+#define UCHCOM_REQ_RESET 0xA1
+#define UCHCOM_REQ_SET_DTRRTS 0xA4
+
+#define UCHCOM_REG_STAT1 0x06
+#define UCHCOM_REG_STAT2 0x07
+#define UCHCOM_REG_BPS_PRE 0x12
+#define UCHCOM_REG_BPS_DIV 0x13
+#define UCHCOM_REG_BPS_MOD 0x14
+#define UCHCOM_REG_BPS_PAD 0x0F
+#define UCHCOM_REG_BREAK1 0x05
+#define UCHCOM_REG_BREAK2 0x18
+#define UCHCOM_REG_LCR1 0x18
+#define UCHCOM_REG_LCR2 0x25
+
+#define UCHCOM_VER_20 0x20
+
+#define UCHCOM_BASE_UNKNOWN 0
+#define UCHCOM_BPS_MOD_BASE 20000000
+#define UCHCOM_BPS_MOD_BASE_OFS 1100
+
+#define UCHCOM_DTR_MASK 0x20
+#define UCHCOM_RTS_MASK 0x40
+
+#define UCHCOM_BRK1_MASK 0x01
+#define UCHCOM_BRK2_MASK 0x40
+
+#define UCHCOM_LCR1_MASK 0xAF
+#define UCHCOM_LCR2_MASK 0x07
+#define UCHCOM_LCR1_PARENB 0x80
+#define UCHCOM_LCR2_PAREVEN 0x07
+#define UCHCOM_LCR2_PARODD 0x06
+#define UCHCOM_LCR2_PARMARK 0x05
+#define UCHCOM_LCR2_PARSPACE 0x04
+
+#define UCHCOM_INTR_STAT1 0x02
+#define UCHCOM_INTR_STAT2 0x03
+#define UCHCOM_INTR_LEAST 4
+
+#define UCHCOMIBUFSIZE 256
+#define UCHCOMOBUFSIZE 256
+
+struct uchcom_softc
+{
+ struct ucom_softc sc_ucom;
+
+ /* */
+ int sc_intr_endpoint;
+ int sc_intr_size;
+ usbd_pipe_handle sc_intr_pipe;
+ u_char *sc_intr_buf;
+ /* */
+ uint8_t sc_version;
+ int sc_dtr;
+ int sc_rts;
+ u_char sc_lsr;
+ u_char sc_msr;
+ int sc_lcr1;
+ int sc_lcr2;
+};
+
+struct uchcom_endpoints
+{
+ int ep_bulkin;
+ int ep_bulkout;
+ int ep_intr;
+ int ep_intr_size;
+};
+
+struct uchcom_divider
+{
+ uint8_t dv_prescaler;
+ uint8_t dv_div;
+ uint8_t dv_mod;
+};
+
+struct uchcom_divider_record
+{
+ uint32_t dvr_high;
+ uint32_t dvr_low;
+ uint32_t dvr_base_clock;
+ struct uchcom_divider dvr_divider;
+};
+
+static const struct uchcom_divider_record dividers[] =
+{
+ { 307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } },
+ { 921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } },
+ { 2999999, 23530, 6000000, { 3, 0, 0 } },
+ { 23529, 2942, 750000, { 2, 0, 0 } },
+ { 2941, 368, 93750, { 1, 0, 0 } },
+ { 367, 1, 11719, { 0, 0, 0 } },
+};
+#define NUM_DIVIDERS (sizeof (dividers) / sizeof (dividers[0]))
+
+static const struct usb_devno uchcom_devs[] = {
+ { USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER },
+};
+#define uchcom_lookup(v, p) usb_lookup(uchcom_devs, v, p)
+
+static void uchcom_get_status(void *, int, u_char *, u_char *);
+static void uchcom_set(void *, int, int, int);
+static int uchcom_param(void *, int, struct termios *);
+static int uchcom_open(void *, int);
+static void uchcom_close(void *, int);
+static void uchcom_intr(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+
+static int set_config(device_t );
+static int find_ifaces(struct uchcom_softc *, usbd_interface_handle *);
+static int find_endpoints(struct uchcom_softc *,
+ struct uchcom_endpoints *);
+static void close_intr_pipe(struct uchcom_softc *);
+
+static int uchcom_match(device_t );
+static int uchcom_attach(device_t );
+static int uchcom_detach(device_t );
+
+struct ucom_callback uchcom_callback = {
+ .ucom_get_status = uchcom_get_status,
+ .ucom_set = uchcom_set,
+ .ucom_param = uchcom_param,
+ .ucom_ioctl = NULL,
+ .ucom_open = uchcom_open,
+ .ucom_close = uchcom_close,
+ .ucom_read = NULL,
+ .ucom_write = NULL,
+};
+
+
+
+
+/* ----------------------------------------------------------------------
+ * driver entry points
+ */
+
+static int uchcom_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ return (uchcom_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+static int uchcom_attach(device_t self)
+{
+ struct uchcom_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+
+ struct uchcom_endpoints endpoints;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+
+ ucom->sc_dying = 0;
+ sc->sc_dtr = sc->sc_rts = -1;
+ sc->sc_lsr = sc->sc_msr = 0;
+
+ DPRINTF(("\n\nuchcom attach: sc=%p\n", sc));
+
+ if (set_config(self))
+ goto failed;
+
+ switch (uaa->release) {
+ case UCHCOM_REV_CH340:
+ device_printf(self, "CH340 detected\n");
+ break;
+ default:
+ device_printf(self, "CH341 detected\n");
+ break;
+ }
+
+ if (find_ifaces(sc, &ucom->sc_iface))
+ goto failed;
+
+ if (find_endpoints(sc, &endpoints))
+ goto failed;
+
+ sc->sc_intr_endpoint = endpoints.ep_intr;
+ sc->sc_intr_size = endpoints.ep_intr_size;
+
+ /* setup ucom layer */
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ ucom->sc_bulkin_no = endpoints.ep_bulkin;
+ ucom->sc_bulkout_no = endpoints.ep_bulkout;
+ ucom->sc_ibufsize = UCHCOMIBUFSIZE;
+ ucom->sc_obufsize = UCHCOMOBUFSIZE;
+ ucom->sc_ibufsizepad = UCHCOMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_parent = sc;
+
+ ucom->sc_callback = &uchcom_callback;
+
+ ucom_attach(&sc->sc_ucom);
+
+ return 0;
+
+failed:
+ ucom->sc_dying = 1;
+ return ENXIO;
+}
+
+static int uchcom_detach(device_t self)
+{
+ struct uchcom_softc *sc = device_get_softc(self);
+ struct ucom_softc *ucom = &sc->sc_ucom ;
+ int rv = 0;
+
+ DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags));
+
+ close_intr_pipe(sc);
+
+ ucom->sc_dying = 1;
+
+ rv = ucom_detach(ucom);
+
+ return rv;
+}
+static int
+set_config(device_t dev)
+{
+ struct uchcom_softc *sc = device_get_softc(dev);
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usbd_status err;
+
+ err = usbd_set_config_index(ucom->sc_udev, UCHCOM_CONFIG_INDEX, 1);
+ if (err) {
+ device_printf(dev, "failed to set configuration: %s\n",
+ usbd_errstr(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+find_ifaces(struct uchcom_softc *sc, usbd_interface_handle *riface)
+{
+ usbd_status err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+
+ err = usbd_device2interface_handle(ucom->sc_udev, UCHCOM_IFACE_INDEX,
+ riface);
+ if (err) {
+ device_printf(ucom->sc_dev, "failed to get interface: %s\n",
+ usbd_errstr(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+find_endpoints(struct uchcom_softc *sc, struct uchcom_endpoints *endpoints)
+{
+ struct ucom_softc *ucom= &sc->sc_ucom;
+ int i, bin=-1, bout=-1, intr=-1, isize=0;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(ucom->sc_dev, "no endpoint descriptor for %d\n", i);
+ return -1;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ intr = ed->bEndpointAddress;
+ isize = UGETW(ed->wMaxPacketSize);
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ bin = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ bout = ed->bEndpointAddress;
+ }
+ }
+
+ if (intr == -1 || bin == -1 || bout == -1) {
+ if (intr == -1) {
+ device_printf(ucom->sc_dev, "no interrupt end point\n");
+ }
+ if (bin == -1) {
+ device_printf(ucom->sc_dev, "no data bulk in end point\n");
+
+ }
+ if (bout == -1) {
+ device_printf(ucom->sc_dev, "no data bulk out end point\n");
+ }
+ return -1;
+ }
+ if (isize < UCHCOM_INTR_LEAST) {
+ device_printf(ucom->sc_dev, "intr pipe is too short");
+ return -1;
+ }
+
+ DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
+ USBDEVNAME(sc->sc_dev), bin, bout, intr, isize));
+
+ endpoints->ep_intr = intr;
+ endpoints->ep_intr_size = isize;
+ endpoints->ep_bulkin = bin;
+ endpoints->ep_bulkout = bout;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------
+ * low level i/o
+ */
+
+static __inline usbd_status
+generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
+ uint16_t value, uint16_t index)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = reqno;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 0);
+
+ return usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+}
+
+static __inline usbd_status
+generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
+ uint16_t value, uint16_t index, void *buf, int buflen,
+ int *actlen)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = reqno;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, (uint16_t)buflen);
+
+ return usbd_do_request_flags(sc->sc_ucom.sc_udev, &req, buf,
+ USBD_SHORT_XFER_OK, actlen,
+ USBD_DEFAULT_TIMEOUT);
+}
+
+static __inline usbd_status
+write_reg(struct uchcom_softc *sc,
+ uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
+{
+ DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
+ (unsigned)reg1, (unsigned)val1,
+ (unsigned)reg2, (unsigned)val2));
+ return generic_control_out(
+ sc, UCHCOM_REQ_WRITE_REG,
+ reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
+}
+
+static __inline usbd_status
+read_reg(struct uchcom_softc *sc,
+ uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
+{
+ uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
+ usbd_status err;
+ int actin;
+
+ err = generic_control_in(
+ sc, UCHCOM_REQ_READ_REG,
+ reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin);
+ if (err)
+ return err;
+
+ DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n",
+ (unsigned)reg1, (unsigned)buf[0],
+ (unsigned)reg2, (unsigned)buf[1]));
+
+ if (rval1) *rval1 = buf[0];
+ if (rval2) *rval2 = buf[1];
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+static __inline usbd_status
+get_version(struct uchcom_softc *sc, uint8_t *rver)
+{
+ uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
+ usbd_status err;
+ int actin;
+
+ err = generic_control_in(
+ sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin);
+ if (err)
+ return err;
+
+ if (rver) *rver = buf[0];
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+static __inline usbd_status
+get_status(struct uchcom_softc *sc, uint8_t *rval)
+{
+ return read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
+}
+
+static __inline usbd_status
+set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
+{
+ return write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
+}
+
+static __inline usbd_status
+set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
+{
+ return generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
+}
+
+
+/* ----------------------------------------------------------------------
+ * middle layer
+ */
+
+static int
+update_version(struct uchcom_softc *sc)
+{
+ usbd_status err;
+
+ err = get_version(sc, &sc->sc_version);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "cannot get version: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+
+ return 0;
+}
+
+static void
+convert_status(struct uchcom_softc *sc, uint8_t cur)
+{
+ sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
+ sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
+
+ cur = ~cur & 0x0F;
+ sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
+}
+
+static int
+update_status(struct uchcom_softc *sc)
+{
+ usbd_status err;
+ uint8_t cur;
+
+ err = get_status(sc, &cur);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev,
+ "cannot update status: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+ convert_status(sc, cur);
+
+ return 0;
+}
+
+
+static int
+set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
+{
+ usbd_status err;
+ uint8_t val = 0;
+
+ if (dtr) val |= UCHCOM_DTR_MASK;
+ if (rts) val |= UCHCOM_RTS_MASK;
+
+ if (sc->sc_version < UCHCOM_VER_20)
+ err = set_dtrrts_10(sc, ~val);
+ else
+ err = set_dtrrts_20(sc, ~val);
+
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "cannot set DTR/RTS: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+
+ return 0;
+}
+
+static int
+set_break(struct uchcom_softc *sc, int onoff)
+{
+ usbd_status err;
+ uint8_t brk1, brk2;
+
+ err = read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2);
+ if (err)
+ return EIO;
+ if (onoff) {
+ /* on - clear bits */
+ brk1 &= ~UCHCOM_BRK1_MASK;
+ brk2 &= ~UCHCOM_BRK2_MASK;
+ } else {
+ /* off - set bits */
+ brk1 |= UCHCOM_BRK1_MASK;
+ brk2 |= UCHCOM_BRK2_MASK;
+ }
+ err = write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2);
+ if (err)
+ return EIO;
+
+ return 0;
+}
+
+static int
+calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
+{
+ int i;
+ const struct uchcom_divider_record *rp;
+ uint32_t div, rem, mod;
+
+ /* find record */
+ for (i=0; i<NUM_DIVIDERS; i++) {
+ if (dividers[i].dvr_high >= rate &&
+ dividers[i].dvr_low <= rate) {
+ rp = &dividers[i];
+ goto found;
+ }
+ }
+ return -1;
+
+found:
+ dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
+ if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
+ dp->dv_div = rp->dvr_divider.dv_div;
+ else {
+ div = rp->dvr_base_clock / rate;
+ rem = rp->dvr_base_clock % rate;
+ if (div==0 || div>=0xFF)
+ return -1;
+ if ((rem<<1) >= rate)
+ div += 1;
+ dp->dv_div = (uint8_t)-div;
+ }
+
+ mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
+ mod = mod + mod/2;
+
+ dp->dv_mod = mod / 0x100;
+
+ return 0;
+}
+
+static int
+set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
+{
+ usbd_status err;
+ struct uchcom_divider dv;
+
+ if (calc_divider_settings(&dv, rate))
+ return EINVAL;
+
+ if ((err = write_reg(sc,
+ UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
+ UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
+ (err = write_reg(sc,
+ UCHCOM_REG_BPS_MOD, dv.dv_mod,
+ UCHCOM_REG_BPS_PAD, 0))) {
+ device_printf(sc->sc_ucom.sc_dev, " cannot set DTE rate: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+
+ return 0;
+}
+
+static int
+set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
+{
+ usbd_status err;
+ uint8_t lcr1 = 0, lcr2 = 0;
+
+ err = read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, " cannot get LCR: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+
+ lcr1 &= ~UCHCOM_LCR1_MASK;
+ lcr2 &= ~UCHCOM_LCR2_MASK;
+
+ /*
+ * XXX: it is difficult to handle the line control appropriately:
+ * - CS8, !CSTOPB and any parity mode seems ok, but
+ * - the chip doesn't have the function to calculate parity
+ * in !CS8 mode.
+ * - it is unclear that the chip supports CS5,6 mode.
+ * - it is unclear how to handle stop bits.
+ */
+
+ switch (ISSET(cflag, CSIZE)) {
+ case CS5:
+ case CS6:
+ case CS7:
+ return EINVAL;
+ case CS8:
+ break;
+ }
+
+ if (ISSET(cflag, PARENB)) {
+ lcr1 |= UCHCOM_LCR1_PARENB;
+ if (ISSET(cflag, PARODD))
+ lcr2 |= UCHCOM_LCR2_PARODD;
+ else
+ lcr2 |= UCHCOM_LCR2_PAREVEN;
+ }
+
+ err = write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2, lcr2);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "cannot set LCR: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+
+ return 0;
+}
+
+static int
+clear_chip(struct uchcom_softc *sc)
+{
+ usbd_status err;
+
+ DPRINTF(("%s: clear\n", USBDEVNAME(sc->sc_dev)));
+ err = generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "cannot clear: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+
+ return 0;
+}
+
+static int
+reset_chip(struct uchcom_softc *sc)
+{
+ usbd_status err;
+ uint8_t lcr1, lcr2, pre, div, mod;
+ uint16_t val=0, idx=0;
+
+ err = read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
+ if (err)
+ goto failed;
+
+ err = read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV, &div);
+ if (err)
+ goto failed;
+
+ err = read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD, NULL);
+ if (err)
+ goto failed;
+
+ val |= (uint16_t)(lcr1&0xF0) << 8;
+ val |= 0x01;
+ val |= (uint16_t)(lcr2&0x0F) << 8;
+ val |= 0x02;
+ idx |= pre & 0x07;
+ val |= 0x04;
+ idx |= (uint16_t)div << 8;
+ val |= 0x08;
+ idx |= mod & 0xF8;
+ val |= 0x10;
+
+ DPRINTF(("%s: reset v=0x%04X, i=0x%04X\n",
+ USBDEVNAME(sc->sc_dev), val, idx));
+
+ err = generic_control_out(sc, UCHCOM_REQ_RESET, val, idx);
+ if (err)
+ goto failed;
+
+ return 0;
+
+failed:
+ device_printf(sc->sc_ucom.sc_dev, "cannot reset: %s\n",
+ usbd_errstr(err));
+ return EIO;
+}
+
+static int
+setup_comm(struct uchcom_softc *sc)
+{
+ int ret;
+
+ ret = update_version(sc);
+ if (ret)
+ return ret;
+
+ ret = clear_chip(sc);
+ if (ret)
+ return ret;
+
+ ret = set_dte_rate(sc, TTYDEF_SPEED);
+ if (ret)
+ return ret;
+
+ ret = set_line_control(sc, CS8);
+ if (ret)
+ return ret;
+
+ ret = update_status(sc);
+ if (ret)
+ return ret;
+
+ ret = reset_chip(sc);
+ if (ret)
+ return ret;
+
+ ret = set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
+ if (ret)
+ return ret;
+
+ sc->sc_dtr = sc->sc_rts = 1;
+ ret = set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+setup_intr_pipe(struct uchcom_softc *sc)
+{
+ usbd_status err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
+ sc->sc_intr_buf = malloc(sc->sc_intr_size, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(ucom->sc_iface,
+ sc->sc_intr_endpoint,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intr_pipe, sc,
+ sc->sc_intr_buf,
+ sc->sc_intr_size,
+ uchcom_intr, USBD_DEFAULT_INTERVAL);
+ if (err) {
+ device_printf(ucom->sc_dev,
+ "cannot open interrupt pipe: %s\n",
+ usbd_errstr(err));
+ return EIO;
+ }
+ }
+ return 0;
+}
+
+static void
+close_intr_pipe(struct uchcom_softc *sc)
+{
+ usbd_status err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ if (ucom->sc_dying)
+ return;
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ device_printf(ucom->sc_dev,
+ "abort interrupt pipe failed: %s\n",
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ device_printf(ucom->sc_dev,
+ " close interrupt pipe failed: %s\n",
+ usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
+
+
+/* ----------------------------------------------------------------------
+ * methods for ucom
+ */
+void
+uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
+{
+ struct uchcom_softc *sc = arg;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ *rlsr = sc->sc_lsr;
+ *rmsr = sc->sc_msr;
+}
+
+void
+uchcom_set(void *arg, int portno, int reg, int onoff)
+{
+ struct uchcom_softc *sc = arg;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ sc->sc_dtr = !!onoff;
+ set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
+ break;
+ case UCOM_SET_RTS:
+ sc->sc_rts = !!onoff;
+ set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
+ break;
+ case UCOM_SET_BREAK:
+ set_break(sc, onoff);
+ break;
+ }
+}
+
+int
+uchcom_param(void *arg, int portno, struct termios *t)
+{
+ struct uchcom_softc *sc = arg;
+ int ret;
+
+ if (sc->sc_ucom.sc_dying)
+ return 0;
+
+ ret = set_line_control(sc, t->c_cflag);
+ if (ret)
+ return ret;
+
+ ret = set_dte_rate(sc, t->c_ospeed);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int
+uchcom_open(void *arg, int portno)
+{
+ int ret;
+ struct uchcom_softc *sc = arg;
+
+ if (sc->sc_ucom.sc_dying)
+ return EIO;
+
+ ret = setup_intr_pipe(sc);
+ if (ret)
+ return ret;
+
+ ret = setup_comm(sc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void
+uchcom_close(void *arg, int portno)
+{
+ struct uchcom_softc *sc = arg;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ close_intr_pipe(sc);
+}
+
+
+/* ----------------------------------------------------------------------
+ * callback when the modem status is changed.
+ */
+void
+uchcom_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct uchcom_softc *sc = priv;
+ u_char *buf = sc->sc_intr_buf;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ DPRINTF(("%s: abnormal status: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(status)));
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+ DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
+ "0x%02X 0x%02X 0x%02X 0x%02X\n",
+ USBDEVNAME(sc->sc_dev),
+ (unsigned)buf[0], (unsigned)buf[1],
+ (unsigned)buf[2], (unsigned)buf[3],
+ (unsigned)buf[4], (unsigned)buf[5],
+ (unsigned)buf[6], (unsigned)buf[7]));
+
+ convert_status(sc, buf[UCHCOM_INTR_STAT1]);
+ ucom_status_change(&sc->sc_ucom);
+}
+
+static device_method_t uchcom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uchcom_match),
+ DEVMETHOD(device_attach, uchcom_attach),
+ DEVMETHOD(device_detach, uchcom_detach),
+
+ { 0, 0 }
+};
+
+static driver_t uchcom_driver = {
+ "ucom",
+ uchcom_methods,
+ sizeof (struct uchcom_softc)
+};
+
+DRIVER_MODULE(uchcom, uhub, uchcom_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uchcom, usb, 1, 1, 1);
+MODULE_DEPEND(uchcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
diff --git a/sys/legacy/dev/usb/ucom.c b/sys/legacy/dev/usb/ucom.c
new file mode 100644
index 0000000..ae263a0
--- /dev/null
+++ b/sys/legacy/dev/usb/ucom.c
@@ -0,0 +1,829 @@
+/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2001-2003, 2005, 2008
+ * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 1998, 2000 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/serial.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef USB_DEBUG
+static int ucomdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
+ &ucomdebug, 0, "ucom debug level");
+#define DPRINTF(x) do { \
+ if (ucomdebug) \
+ printf x; \
+ } while (0)
+
+#define DPRINTFN(n, x) do { \
+ if (ucomdebug > (n)) \
+ printf x; \
+ } while (0)
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+static int ucom_modevent(module_t, int, void *);
+static void ucom_cleanup(struct ucom_softc *);
+static void ucom_shutdown(struct ucom_softc *);
+static void ucom_dtr(struct ucom_softc *, int);
+static void ucom_rts(struct ucom_softc *, int);
+static void ucombreak(struct ucom_softc *, int);
+static usbd_status ucomstartread(struct ucom_softc *);
+static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void ucomstopread(struct ucom_softc *);
+
+static tsw_open_t ucomtty_open;
+static tsw_close_t ucomtty_close;
+static tsw_outwakeup_t ucomtty_outwakeup;
+static tsw_ioctl_t ucomtty_ioctl;
+static tsw_param_t ucomtty_param;
+static tsw_modem_t ucomtty_modem;
+static tsw_free_t ucomtty_free;
+
+static struct ttydevsw ucomtty_class = {
+ .tsw_flags = TF_INITLOCK|TF_CALLOUT,
+ .tsw_open = ucomtty_open,
+ .tsw_close = ucomtty_close,
+ .tsw_outwakeup = ucomtty_outwakeup,
+ .tsw_ioctl = ucomtty_ioctl,
+ .tsw_param = ucomtty_param,
+ .tsw_modem = ucomtty_modem,
+ .tsw_free = ucomtty_free,
+};
+
+devclass_t ucom_devclass;
+
+static moduledata_t ucom_mod = {
+ "ucom",
+ ucom_modevent,
+ NULL
+};
+
+DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+MODULE_DEPEND(ucom, usb, 1, 1, 1);
+MODULE_VERSION(ucom, UCOM_MODVER);
+
+static int
+ucom_modevent(module_t mod, int type, void *data)
+{
+ switch (type) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+void
+ucom_attach_tty(struct ucom_softc *sc, char* fmt, int unit)
+{
+ struct tty *tp;
+
+ sc->sc_tty = tp = tty_alloc(&ucomtty_class, sc, &Giant);
+ tty_makedev(tp, NULL, fmt, unit);
+}
+
+int
+ucom_attach(struct ucom_softc *sc)
+{
+
+ ucom_attach_tty(sc, "U%d", device_get_unit(sc->sc_dev));
+
+ DPRINTF(("ucom_attach: ttycreate: tp = %p, %s\n",
+ sc->sc_tty, sc->sc_tty->t_dev->si_name));
+
+ return (0);
+}
+
+int
+ucom_detach(struct ucom_softc *sc)
+{
+ DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty));
+
+ tty_lock(sc->sc_tty);
+ sc->sc_dying = 1;
+
+ if (sc->sc_bulkin_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (sc->sc_bulkout_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+
+ tty_rel_gone(sc->sc_tty);
+
+ return (0);
+}
+
+static void
+ucom_shutdown(struct ucom_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+
+ DPRINTF(("ucom_shutdown\n"));
+ /*
+ * Hang up if necessary. Wait a bit, so the other side has time to
+ * notice even if we immediately open the port again.
+ */
+ if (tp->t_termios.c_cflag & HUPCL) {
+ (void)ucomtty_modem(tp, 0, SER_DTR);
+#if 0
+ (void)tsleep(sc, TTIPRI, "ucomsd", hz);
+#endif
+ }
+}
+
+static int
+ucomtty_open(struct tty *tp)
+{
+ struct ucom_softc *sc = tty_softc(tp);
+ usbd_status err;
+ int error;
+
+ if (sc->sc_dying)
+ return (ENXIO);
+
+ DPRINTF(("%s: ucomtty_open: tp = %p\n", device_get_nameunit(sc->sc_dev), tp));
+
+ sc->sc_poll = 0;
+ sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;
+
+ (void)ucomtty_modem(tp, SER_DTR | SER_RTS, 0);
+
+ /* Device specific open */
+ if (sc->sc_callback->ucom_open != NULL) {
+ error = sc->sc_callback->ucom_open(sc->sc_parent,
+ sc->sc_portno);
+ if (error) {
+ ucom_cleanup(sc);
+ return (error);
+ }
+ }
+
+ DPRINTF(("ucomtty_open: open pipes in = %d out = %d\n",
+ sc->sc_bulkin_no, sc->sc_bulkout_no));
+
+ /* Open the bulk pipes */
+ /* Bulk-in pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
+ &sc->sc_bulkin_pipe);
+ if (err) {
+ printf("%s: open bulk in error (addr %d): %s\n",
+ device_get_nameunit(sc->sc_dev), sc->sc_bulkin_no,
+ usbd_errstr(err));
+ error = EIO;
+ goto fail;
+ }
+ /* Bulk-out pipe */
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ printf("%s: open bulk out error (addr %d): %s\n",
+ device_get_nameunit(sc->sc_dev), sc->sc_bulkout_no,
+ usbd_errstr(err));
+ error = EIO;
+ goto fail;
+ }
+
+ /* Allocate a request and an input buffer and start reading. */
+ sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_ixfer == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+
+ sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
+ sc->sc_ibufsizepad);
+ if (sc->sc_ibuf == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+
+ sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_oxfer == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+
+ sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
+ sc->sc_obufsize +
+ sc->sc_opkthdrlen);
+ if (sc->sc_obuf == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+
+ sc->sc_state |= UCS_RXSTOP;
+ ucomstartread(sc);
+
+ sc->sc_poll = 1;
+
+ return (0);
+
+fail:
+ ucom_cleanup(sc);
+ return (error);
+}
+
+static void
+ucomtty_close(struct tty *tp)
+{
+ struct ucom_softc *sc = tty_softc(tp);
+
+ DPRINTF(("%s: ucomtty_close \n", device_get_nameunit(sc->sc_dev)));
+
+ ucom_cleanup(sc);
+
+ if (sc->sc_callback->ucom_close != NULL)
+ sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno);
+}
+
+static int
+ucomtty_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *p)
+{
+ struct ucom_softc *sc = tty_softc(tp);
+ int error;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("ucomioctl: cmd = 0x%08lx\n", cmd));
+
+ switch (cmd) {
+ case TIOCSBRK:
+ ucombreak(sc, 1);
+ return (0);
+ case TIOCCBRK:
+ ucombreak(sc, 0);
+ return (0);
+ }
+
+ error = ENOIOCTL;
+ if (sc->sc_callback->ucom_ioctl != NULL)
+ error = sc->sc_callback->ucom_ioctl(sc->sc_parent,
+ sc->sc_portno,
+ cmd, data, p);
+ return (error);
+}
+
+static int
+ucomtty_modem(struct tty *tp, int sigon, int sigoff)
+{
+ struct ucom_softc *sc = tty_softc(tp);
+ int mcr;
+ int msr;
+ int onoff;
+
+ if (sigon == 0 && sigoff == 0) {
+ mcr = sc->sc_mcr;
+ if (ISSET(mcr, SER_DTR))
+ sigon |= SER_DTR;
+ if (ISSET(mcr, SER_RTS))
+ sigon |= SER_RTS;
+
+ msr = sc->sc_msr;
+ if (ISSET(msr, SER_CTS))
+ sigon |= SER_CTS;
+ if (ISSET(msr, SER_DCD))
+ sigon |= SER_DCD;
+ if (ISSET(msr, SER_DSR))
+ sigon |= SER_DSR;
+ if (ISSET(msr, SER_RI))
+ sigon |= SER_RI;
+ return (sigon);
+ }
+
+ mcr = sc->sc_mcr;
+ if (ISSET(sigon, SER_DTR))
+ mcr |= SER_DTR;
+ if (ISSET(sigoff, SER_DTR))
+ mcr &= ~SER_DTR;
+ if (ISSET(sigon, SER_RTS))
+ mcr |= SER_RTS;
+ if (ISSET(sigoff, SER_RTS))
+ mcr &= ~SER_RTS;
+ sc->sc_mcr = mcr;
+
+ onoff = ISSET(sc->sc_mcr, SER_DTR) ? 1 : 0;
+ ucom_dtr(sc, onoff);
+
+ onoff = ISSET(sc->sc_mcr, SER_RTS) ? 1 : 0;
+ ucom_rts(sc, onoff);
+
+ return (0);
+}
+
+static void
+ucombreak(struct ucom_softc *sc, int onoff)
+{
+ DPRINTF(("ucombreak: onoff = %d\n", onoff));
+
+ if (sc->sc_callback->ucom_set == NULL)
+ return;
+ sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
+ UCOM_SET_BREAK, onoff);
+}
+
+static void
+ucom_dtr(struct ucom_softc *sc, int onoff)
+{
+ DPRINTF(("ucom_dtr: onoff = %d\n", onoff));
+
+ if (sc->sc_callback->ucom_set == NULL)
+ return;
+ sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
+ UCOM_SET_DTR, onoff);
+}
+
+static void
+ucom_rts(struct ucom_softc *sc, int onoff)
+{
+ DPRINTF(("ucom_rts: onoff = %d\n", onoff));
+
+ if (sc->sc_callback->ucom_set == NULL)
+ return;
+ sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
+ UCOM_SET_RTS, onoff);
+}
+
+void
+ucom_status_change(struct ucom_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+ u_char old_msr;
+ int onoff;
+
+ if (sc->sc_callback->ucom_get_status == NULL) {
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+ return;
+ }
+
+ old_msr = sc->sc_msr;
+ sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno,
+ &sc->sc_lsr, &sc->sc_msr);
+ if (ISSET((sc->sc_msr ^ old_msr), SER_DCD)) {
+ if (sc->sc_poll == 0)
+ return;
+ onoff = ISSET(sc->sc_msr, SER_DCD) ? 1 : 0;
+ DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff));
+ ttydisc_modem(tp, onoff);
+ }
+}
+
+static int
+ucomtty_param(struct tty *tp, struct termios *t)
+{
+ struct ucom_softc *sc = tty_softc(tp);
+ int error;
+ usbd_status uerr;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("ucomtty_param: sc = %p\n", sc));
+
+ /* Check requested parameters. */
+ if (t->c_ospeed < 0) {
+ DPRINTF(("ucomtty_param: negative ospeed\n"));
+ return (EINVAL);
+ }
+ if (t->c_ispeed && t->c_ispeed != t->c_ospeed) {
+ DPRINTF(("ucomtty_param: mismatch ispeed and ospeed\n"));
+ return (EINVAL);
+ }
+ t->c_ispeed = t->c_ospeed;
+
+ if (sc->sc_callback->ucom_param == NULL)
+ return (0);
+
+ ucomstopread(sc);
+
+ error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t);
+ if (error) {
+ DPRINTF(("ucomtty_param: callback: error = %d\n", error));
+ return (error);
+ }
+
+#if 0
+ ttsetwater(tp);
+#endif
+
+ if (t->c_cflag & CRTS_IFLOW) {
+ sc->sc_state |= UCS_RTS_IFLOW;
+ } else if (sc->sc_state & UCS_RTS_IFLOW) {
+ sc->sc_state &= ~UCS_RTS_IFLOW;
+ (void)ucomtty_modem(tp, SER_RTS, 0);
+ }
+
+#if 0
+ ttyldoptim(tp);
+#endif
+
+ uerr = ucomstartread(sc);
+ if (uerr != USBD_NORMAL_COMPLETION)
+ return (EIO);
+
+ return (0);
+}
+
+static void
+ucomtty_free(void *sc)
+{
+ /*
+ * Our softc gets deallocated earlier on.
+ * XXX: we should make sure the TTY device name doesn't get
+ * recycled before we end up here!
+ */
+}
+
+static void
+ucomtty_outwakeup(struct tty *tp)
+{
+ struct ucom_softc *sc = tty_softc(tp);
+ usbd_status err;
+ size_t cnt;
+
+ DPRINTF(("ucomtty_outwakeup: sc = %p\n", sc));
+
+ if (sc->sc_dying)
+ return;
+
+ /*
+ * If there's no sc_oxfer, then ucomclose has removed it. The buffer
+ * has just been flushed in the ttyflush() in ttyclose(). ttyflush()
+ * then calls tt_stop(). ucomstop calls ucomstart, so the right thing
+ * to do here is just abort if sc_oxfer is NULL, as everything else
+ * is cleaned up elsewhere.
+ */
+ if (sc->sc_oxfer == NULL)
+ return;
+
+ /* XXX: hardware flow control. We should use inwakeup here. */
+#if 0
+ if (tp->t_state & TS_TBLOCK) {
+ if (ISSET(sc->sc_mcr, SER_RTS) &&
+ ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
+ DPRINTF(("ucomtty_outwakeup: clear RTS\n"));
+ (void)ucomtty_modem(tp, 0, SER_RTS);
+ }
+ } else {
+ if (!ISSET(sc->sc_mcr, SER_RTS) &&
+ tp->t_rawq.c_cc <= tp->t_ilowat &&
+ ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
+ DPRINTF(("ucomtty_outwakeup: set RTS\n"));
+ (void)ucomtty_modem(tp, SER_RTS, 0);
+ }
+ }
+#endif
+
+ if (sc->sc_state & UCS_TXBUSY)
+ return;
+
+ sc->sc_state |= UCS_TXBUSY;
+ if (sc->sc_callback->ucom_write != NULL)
+ cnt = sc->sc_callback->ucom_write(sc->sc_parent,
+ sc->sc_portno, tp, sc->sc_obuf, sc->sc_obufsize);
+ else
+ cnt = ttydisc_getc(tp, sc->sc_obuf, sc->sc_obufsize);
+
+ if (cnt == 0) {
+ DPRINTF(("ucomtty_outwakeup: cnt == 0\n"));
+ sc->sc_state &= ~UCS_TXBUSY;
+ return;
+ }
+ sc->sc_obufactive = cnt;
+
+ DPRINTF(("ucomtty_outwakeup: %zu chars\n", cnt));
+ usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
+ (usbd_private_handle)sc, sc->sc_obuf, cnt,
+ USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
+ /* What can we do on error? */
+ err = usbd_transfer(sc->sc_oxfer);
+ if (err != USBD_IN_PROGRESS) {
+ printf("ucomtty_outwakeup: err=%s\n", usbd_errstr(err));
+ sc->sc_state &= ~UCS_TXBUSY;
+ }
+}
+
+#if 0
+static void
+ucomstop(struct tty *tp, int flag)
+{
+ struct ucom_softc *sc = tty_softc(tp);
+ int s;
+
+ DPRINTF(("ucomstop: %d\n", flag));
+
+ if ((flag & FREAD) && (sc->sc_state & UCS_RXSTOP) == 0) {
+ DPRINTF(("ucomstop: read\n"));
+ ucomstopread(sc);
+ ucomstartread(sc);
+ }
+
+ if (flag & FWRITE) {
+ DPRINTF(("ucomstop: write\n"));
+ if (ISSET(tp->t_state, TS_BUSY)) {
+ /* XXX do what? */
+ if (!ISSET(tp->t_state, TS_TTSTOP))
+ SET(tp->t_state, TS_FLUSH);
+ }
+ }
+
+ ucomtty_outwakeup(tp);
+
+ DPRINTF(("ucomstop: done\n"));
+}
+#endif
+
+static void
+ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct ucom_softc *sc = (struct ucom_softc *)p;
+ struct tty *tp = sc->sc_tty;
+ u_int32_t cc;
+
+ DPRINTF(("ucomwritecb: status = %d\n", status));
+
+ if (status == USBD_CANCELLED || sc->sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ printf("%s: ucomwritecb: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ /* XXX we should restart after some delay. */
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
+ DPRINTF(("ucomwritecb: cc = %d\n", cc));
+ if (cc <= sc->sc_opkthdrlen) {
+ printf("%s: sent size too small, cc = %d\n",
+ device_get_nameunit(sc->sc_dev), cc);
+ return;
+ }
+
+ /* convert from USB bytes to tty bytes */
+ cc -= sc->sc_opkthdrlen;
+ if (cc != sc->sc_obufactive)
+ panic("Partial write of %d of %d bytes, not supported\n",
+ cc, sc->sc_obufactive);
+
+ sc->sc_state &= ~UCS_TXBUSY;
+#if 0
+ CLR(tp->t_state, TS_BUSY);
+ if (ISSET(tp->t_state, TS_FLUSH))
+ CLR(tp->t_state, TS_FLUSH);
+ else
+ ndflush(&tp->t_outq, cc);
+#endif
+ ucomtty_outwakeup(tp);
+}
+
+static usbd_status
+ucomstartread(struct ucom_softc *sc)
+{
+ usbd_status err;
+
+ DPRINTF(("ucomstartread: start\n"));
+
+ if (sc->sc_bulkin_pipe == NULL || (sc->sc_state & UCS_RXSTOP) == 0)
+ return (USBD_NORMAL_COMPLETION);
+ sc->sc_state &= ~UCS_RXSTOP;
+
+ usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
+ (usbd_private_handle)sc,
+ sc->sc_ibuf, sc->sc_ibufsize,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, ucomreadcb);
+
+ err = usbd_transfer(sc->sc_ixfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ sc->sc_state |= UCS_RXSTOP;
+ DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+ucomrxchars(struct ucom_softc *sc, u_char *cp, u_int32_t cc)
+{
+ struct tty *tp = sc->sc_tty;
+
+ /* Give characters to tty layer. */
+ if (ttydisc_can_bypass(tp)) {
+ DPRINTFN(7, ("ucomreadcb: buf = %*D\n", cc, cp, ""));
+ cc -= ttydisc_rint_bypass(tp, cp, cc);
+ } else {
+ while (cc > 0) {
+ DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
+ if (ttydisc_rint(tp, *cp, 0) == -1)
+ break;
+ cc--;
+ cp++;
+ }
+ }
+ if (cc > 0)
+ device_printf(sc->sc_dev, "lost %d chars\n", cc);
+ ttydisc_rint_done(tp);
+}
+
+static void
+ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
+{
+ struct ucom_softc *sc = (struct ucom_softc *)p;
+ struct tty *tp = sc->sc_tty;
+ usbd_status err;
+ u_int32_t cc;
+ u_char *cp;
+
+ (void)tp; /* Used for debugging */
+ DPRINTF(("ucomreadcb: status = %d\n", status));
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (!(sc->sc_state & UCS_RXSTOP))
+ printf("%s: ucomreadcb: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(status));
+ sc->sc_state |= UCS_RXSTOP;
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
+ /* XXX we should restart after some delay. */
+ return;
+ }
+ sc->sc_state |= UCS_RXSTOP;
+
+ usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
+ DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
+ if (cc == 0)
+ goto resubmit;
+
+ if (sc->sc_callback->ucom_read != NULL)
+ sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
+ &cp, &cc);
+
+ if (cc > sc->sc_ibufsize) {
+ printf("%s: invalid receive data size, %d chars\n",
+ device_get_nameunit(sc->sc_dev), cc);
+ goto resubmit;
+ }
+ if (cc > 0)
+ ucomrxchars(sc, cp, cc);
+
+ resubmit:
+ err = ucomstartread(sc);
+ if (err) {
+ printf("%s: read start failed\n", device_get_nameunit(sc->sc_dev));
+ /* XXX what should we dow now? */
+ }
+
+#if 0
+ if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, SER_RTS)
+ && !(tp->t_state & TS_TBLOCK))
+ ucomtty_modem(tp, SER_RTS, 0);
+#endif
+}
+
+static void
+ucom_cleanup(struct ucom_softc *sc)
+{
+ DPRINTF(("ucom_cleanup: closing pipes\n"));
+
+ ucom_shutdown(sc);
+ if (sc->sc_bulkin_pipe != NULL) {
+ sc->sc_state |= UCS_RXSTOP;
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+ }
+ if (sc->sc_bulkout_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+ }
+ if (sc->sc_ixfer != NULL) {
+ usbd_free_xfer(sc->sc_ixfer);
+ sc->sc_ixfer = NULL;
+ }
+ if (sc->sc_oxfer != NULL) {
+ usbd_free_xfer(sc->sc_oxfer);
+ sc->sc_oxfer = NULL;
+ }
+}
+
+static void
+ucomstopread(struct ucom_softc *sc)
+{
+ usbd_status err;
+
+ DPRINTF(("ucomstopread: enter\n"));
+
+ if (!(sc->sc_state & UCS_RXSTOP)) {
+ sc->sc_state |= UCS_RXSTOP;
+ if (sc->sc_bulkin_pipe == NULL) {
+ DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
+ return;
+ }
+ err = usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (err) {
+ DPRINTF(("ucomstopread: err = %s\n",
+ usbd_errstr(err)));
+ }
+ }
+
+ DPRINTF(("ucomstopread: leave\n"));
+}
diff --git a/sys/legacy/dev/usb/ucomvar.h b/sys/legacy/dev/usb/ucomvar.h
new file mode 100644
index 0000000..4433a1a
--- /dev/null
+++ b/sys/legacy/dev/usb/ucomvar.h
@@ -0,0 +1,169 @@
+/* $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*-
+ * Copyright (c) 1999 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.
+ */
+
+/* Module interface related macros */
+#define UCOM_MODVER 1
+
+#define UCOM_MINVER 1
+#define UCOM_PREFVER UCOM_MODVER
+#define UCOM_MAXVER 1
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~((unsigned)(f))
+#define ISSET(t, f) ((t) & (f))
+
+#define UCOM_CALLOUT_MASK 0x80
+
+#define UCOMUNIT_MASK 0x3ff7f
+#define UCOMDIALOUT_MASK 0x80000
+#define UCOMCALLUNIT_MASK 0x40000
+
+#define UCOMUNIT(x) (dev2unit(x) & UCOMUNIT_MASK)
+#define UCOMDIALOUT(x) (dev2unit(x) & UCOMDIALOUT_MASK)
+#define UCOMCALLUNIT(x) (dev2unit(x) & UCOMCALLUNIT_MASK)
+
+#define UCOM_UNK_PORTNO -1 /* XXX */
+
+struct tty;
+struct ucom_softc;
+
+struct ucom_callback {
+ void (*ucom_get_status)(void *, int, u_char *, u_char *);
+ void (*ucom_set)(void *, int, int, int);
+#define UCOM_SET_DTR 1
+#define UCOM_SET_RTS 2
+#define UCOM_SET_BREAK 3
+ int (*ucom_param)(void *, int, struct termios *);
+ int (*ucom_ioctl)(void *, int, u_long, caddr_t, struct thread *);
+ int (*ucom_open)(void *, int);
+ void (*ucom_close)(void *, int);
+ void (*ucom_read)(void *, int, u_char **, u_int32_t *);
+ size_t (*ucom_write)(void *, int, struct tty *, u_char *, u_int32_t);
+};
+
+/* line status register */
+#define ULSR_RCV_FIFO 0x80
+#define ULSR_TSRE 0x40 /* Transmitter empty: byte sent */
+#define ULSR_TXRDY 0x20 /* Transmitter buffer empty */
+#define ULSR_BI 0x10 /* Break detected */
+#define ULSR_FE 0x08 /* Framing error: bad stop bit */
+#define ULSR_PE 0x04 /* Parity error */
+#define ULSR_OE 0x02 /* Overrun, lost incoming byte */
+#define ULSR_RXRDY 0x01 /* Byte ready in Receive Buffer */
+#define ULSR_RCV_MASK 0x1f /* Mask for incoming data or error */
+
+/* ucom state declarations */
+#define UCS_RXSTOP 0x0001 /* Rx stopped */
+#define UCS_TXBUSY 0x0002 /* Tx busy */
+#define UCS_RTS_IFLOW 0x0008 /* use RTS input flow control */
+
+struct ucom_softc {
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_udev; /* USB device */
+ usbd_interface_handle sc_iface; /* data interface */
+
+ int sc_bulkin_no; /* bulk in endpoint address */
+ usbd_pipe_handle sc_bulkin_pipe; /* bulk in pipe */
+ usbd_xfer_handle sc_ixfer; /* read request */
+ u_char *sc_ibuf; /* read buffer */
+ u_int sc_ibufsize; /* read buffer size */
+ u_int sc_ibufsizepad; /* read buffer size padded */
+
+ int sc_bulkout_no; /* bulk out endpoint address */
+ usbd_pipe_handle sc_bulkout_pipe;/* bulk out pipe */
+ usbd_xfer_handle sc_oxfer; /* write request */
+ u_char *sc_obuf; /* write buffer */
+ u_int sc_obufsize; /* write buffer size */
+ u_int sc_opkthdrlen; /* header length of
+ output packet */
+ u_int sc_obufactive; /* Active bytes in buffer */
+
+ struct ucom_callback *sc_callback;
+ void *sc_parent;
+ int sc_portno;
+
+ struct tty *sc_tty; /* our tty */
+
+ int sc_state;
+
+ int sc_poll;
+
+ u_char sc_lsr;
+ u_char sc_msr;
+ u_char sc_mcr;
+
+ u_char sc_dying; /* disconnecting */
+
+};
+
+extern devclass_t ucom_devclass;
+
+void ucom_attach_tty(struct ucom_softc *, char*, int);
+int ucom_attach(struct ucom_softc *);
+int ucom_detach(struct ucom_softc *);
+void ucom_status_change(struct ucom_softc *);
+void ucomrxchars(struct ucom_softc *sc, u_char *cp, u_int32_t cc);
diff --git a/sys/legacy/dev/usb/ucycom.c b/sys/legacy/dev/usb/ucycom.c
new file mode 100644
index 0000000..ecf115f
--- /dev/null
+++ b/sys/legacy/dev/usb/ucycom.c
@@ -0,0 +1,543 @@
+/*-
+ * Copyright (c) 2004 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
+ * RS232 bridges.
+ *
+ * Normally, a driver for a USB-to-serial chip would hang off the ucom(4)
+ * driver, but ucom(4) was written under the assumption that all USB-to-
+ * serial chips use bulk pipes for I/O, while the Cypress parts use HID
+ * reports.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+#include <sys/tty.h>
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/hid.h>
+
+#define UCYCOM_EP_INPUT 0
+#define UCYCOM_EP_OUTPUT 1
+
+#define UCYCOM_MAX_IOLEN 32U
+
+struct ucycom_softc {
+ device_t sc_dev;
+ struct tty *sc_tty;
+ int sc_error;
+ unsigned long sc_cintr;
+ unsigned long sc_cin;
+ unsigned long sc_clost;
+ unsigned long sc_cout;
+
+ /* usb parameters */
+ usbd_device_handle sc_usbdev;
+ usbd_interface_handle sc_iface;
+ usbd_pipe_handle sc_pipe;
+ uint8_t sc_iep; /* input endpoint */
+ uint8_t sc_fid; /* feature report id*/
+ uint8_t sc_iid; /* input report id */
+ uint8_t sc_oid; /* output report id */
+ size_t sc_flen; /* feature report length */
+ size_t sc_ilen; /* input report length */
+ size_t sc_olen; /* output report length */
+ uint8_t sc_ibuf[UCYCOM_MAX_IOLEN];
+
+ /* model and settings */
+ uint32_t sc_model;
+#define MODEL_CY7C63743 0x63743
+#define MODEL_CY7C64013 0x64013
+ uint32_t sc_baud;
+ uint8_t sc_cfg;
+#define UCYCOM_CFG_RESET 0x80
+#define UCYCOM_CFG_PARODD 0x20
+#define UCYCOM_CFG_PAREN 0x10
+#define UCYCOM_CFG_STOPB 0x08
+#define UCYCOM_CFG_DATAB 0x03
+ uint8_t sc_ist; /* status flags from last input */
+ uint8_t sc_ost; /* status flags for next output */
+
+ /* flags */
+ char sc_dying;
+};
+
+static device_probe_t ucycom_probe;
+static device_attach_t ucycom_attach;
+static device_detach_t ucycom_detach;
+static t_open_t ucycom_open;
+static t_close_t ucycom_close;
+static void ucycom_start(struct tty *);
+static void ucycom_stop(struct tty *, int);
+static int ucycom_param(struct tty *, struct termios *);
+static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t);
+static void ucycom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+
+static device_method_t ucycom_methods[] = {
+ DEVMETHOD(device_probe, ucycom_probe),
+ DEVMETHOD(device_attach, ucycom_attach),
+ DEVMETHOD(device_detach, ucycom_detach),
+ { 0, 0 }
+};
+
+static driver_t ucycom_driver = {
+ "ucycom",
+ ucycom_methods,
+ sizeof(struct ucycom_softc),
+};
+
+static devclass_t ucycom_devclass;
+
+DRIVER_MODULE(ucycom, uhub, ucycom_driver, ucycom_devclass, usbd_driver_load, 0);
+MODULE_VERSION(ucycom, 1);
+MODULE_DEPEND(ucycom, usb, 1, 1, 1);
+
+/*
+ * Supported devices
+ */
+
+static struct ucycom_device {
+ uint16_t vendor;
+ uint16_t product;
+ uint32_t model;
+} ucycom_devices[] = {
+ { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013 },
+ { 0, 0, 0 },
+};
+
+#define UCYCOM_DEFAULT_RATE 4800
+#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */
+
+/*****************************************************************************
+ *
+ * Driver interface
+ *
+ */
+
+static int
+ucycom_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa;
+ struct ucycom_device *ud;
+
+ uaa = device_get_ivars(dev);
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+ for (ud = ucycom_devices; ud->model != 0; ++ud)
+ if (ud->vendor == uaa->vendor && ud->product == uaa->product)
+ return (UMATCH_VENDOR_PRODUCT);
+ return (UMATCH_NONE);
+}
+
+static int
+ucycom_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa;
+ struct ucycom_softc *sc;
+ struct ucycom_device *ud;
+ usb_endpoint_descriptor_t *ued;
+ void *urd;
+ int error, urdlen;
+
+ /* get arguments and softc */
+ uaa = device_get_ivars(dev);
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof *sc);
+ sc->sc_dev = dev;
+ sc->sc_usbdev = uaa->device;
+
+ /* get chip model */
+ for (ud = ucycom_devices; ud->model != 0; ++ud)
+ if (ud->vendor == uaa->vendor && ud->product == uaa->product)
+ sc->sc_model = ud->model;
+ if (sc->sc_model == 0) {
+ device_printf(dev, "unsupported device\n");
+ return (ENXIO);
+ }
+ device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model);
+
+ /* select configuration */
+ error = usbd_set_config_index(sc->sc_usbdev, 0, 1 /* verbose */);
+ if (error != 0) {
+ device_printf(dev, "failed to select configuration: %s\n",
+ usbd_errstr(error));
+ return (ENXIO);
+ }
+
+ /* get first interface handle */
+ error = usbd_device2interface_handle(sc->sc_usbdev, 0, &sc->sc_iface);
+ if (error != 0) {
+ device_printf(dev, "failed to get interface handle: %s\n",
+ usbd_errstr(error));
+ return (ENXIO);
+ }
+
+ /* get report descriptor */
+ error = usbd_read_report_desc(sc->sc_iface, &urd, &urdlen, M_USBDEV);
+ if (error != 0) {
+ device_printf(dev, "failed to get report descriptor: %s\n",
+ usbd_errstr(error));
+ return (ENXIO);
+ }
+
+ /* get report sizes */
+ sc->sc_flen = hid_report_size(urd, urdlen, hid_feature, &sc->sc_fid);
+ sc->sc_ilen = hid_report_size(urd, urdlen, hid_input, &sc->sc_iid);
+ sc->sc_olen = hid_report_size(urd, urdlen, hid_output, &sc->sc_oid);
+
+ if (sc->sc_ilen > UCYCOM_MAX_IOLEN || sc->sc_olen > UCYCOM_MAX_IOLEN) {
+ device_printf(dev, "I/O report size too big (%zu, %zu, %u)\n",
+ sc->sc_ilen, sc->sc_olen, UCYCOM_MAX_IOLEN);
+ return (ENXIO);
+ }
+
+ /* get and verify input endpoint descriptor */
+ ued = usbd_interface2endpoint_descriptor(sc->sc_iface, UCYCOM_EP_INPUT);
+ if (ued == NULL) {
+ device_printf(dev, "failed to get input endpoint descriptor\n");
+ return (ENXIO);
+ }
+ if (UE_GET_DIR(ued->bEndpointAddress) != UE_DIR_IN) {
+ device_printf(dev, "not an input endpoint\n");
+ return (ENXIO);
+ }
+ if ((ued->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ device_printf(dev, "not an interrupt endpoint\n");
+ return (ENXIO);
+ }
+ sc->sc_iep = ued->bEndpointAddress;
+
+ /* set up tty */
+ sc->sc_tty = ttyalloc();
+ sc->sc_tty->t_sc = sc;
+ sc->sc_tty->t_oproc = ucycom_start;
+ sc->sc_tty->t_stop = ucycom_stop;
+ sc->sc_tty->t_param = ucycom_param;
+ sc->sc_tty->t_open = ucycom_open;
+ sc->sc_tty->t_close = ucycom_close;
+
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "intr", CTLFLAG_RD, &sc->sc_cintr, 0,
+ "interrupt count");
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "in", CTLFLAG_RD, &sc->sc_cin, 0,
+ "input bytes read");
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "lost", CTLFLAG_RD, &sc->sc_clost, 0,
+ "input bytes lost");
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "out", CTLFLAG_RD, &sc->sc_cout, 0,
+ "output bytes");
+
+ /* create character device node */
+ ttycreate(sc->sc_tty, 0, "y%r", device_get_unit(sc->sc_dev));
+
+ return (0);
+}
+
+static int
+ucycom_detach(device_t dev)
+{
+ struct ucycom_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ ttyfree(sc->sc_tty);
+
+ return (0);
+}
+
+/*****************************************************************************
+ *
+ * Device interface
+ *
+ */
+
+static int
+ucycom_open(struct tty *tp, struct cdev *cdev)
+{
+ struct ucycom_softc *sc = tp->t_sc;
+ int error;
+
+ /* set default configuration */
+ ucycom_configure(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG);
+
+ /* open interrupt pipe */
+ error = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep, 0,
+ &sc->sc_pipe, sc, sc->sc_ibuf, sc->sc_ilen,
+ ucycom_intr, USBD_DEFAULT_INTERVAL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "failed to open interrupt pipe: %s\n",
+ usbd_errstr(error));
+ return (ENXIO);
+ }
+
+ if (bootverbose)
+ device_printf(sc->sc_dev, "%s bypass l_rint()\n",
+ (sc->sc_tty->t_state & TS_CAN_BYPASS_L_RINT) ?
+ "can" : "can't");
+
+ /* done! */
+ return (0);
+}
+
+static void
+ucycom_close(struct tty *tp)
+{
+ struct ucycom_softc *sc = tp->t_sc;
+
+ /* stop interrupts and close the interrupt pipe */
+ usbd_abort_pipe(sc->sc_pipe);
+ usbd_close_pipe(sc->sc_pipe);
+ sc->sc_pipe = 0;
+
+ return;
+}
+
+/*****************************************************************************
+ *
+ * TTY interface
+ *
+ */
+
+static void
+ucycom_start(struct tty *tty)
+{
+ struct ucycom_softc *sc = tty->t_sc;
+ uint8_t report[sc->sc_olen];
+ int error, len;
+
+ while (sc->sc_error == 0 && sc->sc_tty->t_outq.c_cc > 0) {
+ switch (sc->sc_model) {
+ case MODEL_CY7C63743:
+ len = q_to_b(&sc->sc_tty->t_outq,
+ report + 1, sc->sc_olen - 1);
+ sc->sc_cout += len;
+ report[0] = len;
+ len += 1;
+ break;
+ case MODEL_CY7C64013:
+ len = q_to_b(&sc->sc_tty->t_outq,
+ report + 2, sc->sc_olen - 2);
+ sc->sc_cout += len;
+ report[0] = 0;
+ report[1] = len;
+ len += 2;
+ break;
+ default:
+ panic("unsupported model (driver error)");
+ }
+
+ while (len < sc->sc_olen)
+ report[len++] = 0;
+ error = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
+ sc->sc_oid, report, sc->sc_olen);
+#if 0
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "failed to set output report: %s\n",
+ usbd_errstr(error));
+ sc->sc_error = error;
+ }
+#endif
+ }
+}
+
+static void
+ucycom_stop(struct tty *tty, int flags)
+{
+ struct ucycom_softc *sc;
+
+ sc = tty->t_sc;
+ if (bootverbose)
+ device_printf(sc->sc_dev, "%s()\n", __func__);
+}
+
+static int
+ucycom_param(struct tty *tty, struct termios *t)
+{
+ struct ucycom_softc *sc;
+ uint32_t baud;
+ uint8_t cfg;
+ int error;
+
+ sc = tty->t_sc;
+
+ if (t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+ baud = t->c_ispeed;
+
+ if (t->c_cflag & CIGNORE) {
+ cfg = sc->sc_cfg;
+ } else {
+ cfg = 0;
+ switch (t->c_cflag & CSIZE) {
+ case CS8:
+ ++cfg;
+ case CS7:
+ ++cfg;
+ case CS6:
+ ++cfg;
+ case CS5:
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (t->c_cflag & CSTOPB)
+ cfg |= UCYCOM_CFG_STOPB;
+ if (t->c_cflag & PARENB)
+ cfg |= UCYCOM_CFG_PAREN;
+ if (t->c_cflag & PARODD)
+ cfg |= UCYCOM_CFG_PARODD;
+ }
+
+ error = ucycom_configure(sc, baud, cfg);
+ return (error);
+}
+
+/*****************************************************************************
+ *
+ * Hardware interface
+ *
+ */
+
+static int
+ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
+{
+ uint8_t report[sc->sc_flen];
+ int error;
+
+ switch (baud) {
+ case 600:
+ case 1200:
+ case 2400:
+ case 4800:
+ case 9600:
+ case 19200:
+ case 38400:
+ case 57600:
+#if 0
+ /*
+ * Stock chips only support standard baud rates in the 600 - 57600
+ * range, but higher rates can be achieved using custom firmware.
+ */
+ case 115200:
+ case 153600:
+ case 192000:
+#endif
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (bootverbose)
+ device_printf(sc->sc_dev, "%d baud, %c-%d-%d\n", baud,
+ (cfg & UCYCOM_CFG_PAREN) ?
+ ((cfg & UCYCOM_CFG_PARODD) ? 'O' : 'E') : 'N',
+ 5 + (cfg & UCYCOM_CFG_DATAB),
+ (cfg & UCYCOM_CFG_STOPB) ? 2 : 1);
+ report[0] = baud & 0xff;
+ report[1] = (baud >> 8) & 0xff;
+ report[2] = (baud >> 16) & 0xff;
+ report[3] = (baud >> 24) & 0xff;
+ report[4] = cfg;
+ error = usbd_set_report(sc->sc_iface, UHID_FEATURE_REPORT,
+ sc->sc_fid, report, sc->sc_flen);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "%s\n", usbd_errstr(error));
+ return (EIO);
+ }
+ sc->sc_baud = baud;
+ sc->sc_cfg = cfg;
+ return (0);
+}
+
+static void
+ucycom_intr(usbd_xfer_handle xfer, usbd_private_handle scp, usbd_status status)
+{
+ struct ucycom_softc *sc = scp;
+ uint8_t *data;
+ int i, len, lost;
+
+ sc->sc_cintr++;
+
+ switch (sc->sc_model) {
+ case MODEL_CY7C63743:
+ sc->sc_ist = sc->sc_ibuf[0] & ~0x07;
+ len = sc->sc_ibuf[0] & 0x07;
+ data = sc->sc_ibuf + 1;
+ break;
+ case MODEL_CY7C64013:
+ sc->sc_ist = sc->sc_ibuf[0] & ~0x07;
+ len = sc->sc_ibuf[1];
+ data = sc->sc_ibuf + 2;
+ break;
+ default:
+ panic("unsupported model (driver error)");
+ }
+
+ switch (status) {
+ case USBD_NORMAL_COMPLETION:
+ break;
+ default:
+ /* XXX */
+ return;
+ }
+
+ if (sc->sc_tty->t_state & TS_CAN_BYPASS_L_RINT) {
+ /* XXX flow control! */
+ lost = b_to_q(data, len, &sc->sc_tty->t_rawq);
+ sc->sc_tty->t_rawcc += len - lost;
+ ttwakeup(sc->sc_tty);
+ } else {
+ for (i = 0, lost = len; i < len; ++i, --lost)
+ if (ttyld_rint(sc->sc_tty, data[i]) != 0)
+ break;
+ }
+ sc->sc_cin += len - lost;
+ sc->sc_clost += lost;
+}
diff --git a/sys/legacy/dev/usb/udbp.c b/sys/legacy/dev/usb/udbp.c
new file mode 100644
index 0000000..523184f
--- /dev/null
+++ b/sys/legacy/dev/usb/udbp.c
@@ -0,0 +1,860 @@
+/*-
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * 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. Neither the name of author 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 NICK HIBMA 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 AUTHOR 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Driver for arbitrary double bulk pipe devices.
+ * The driver assumes that there will be the same driver on the other side.
+ *
+ * XXX Some more information on what the framing of the IP packets looks like.
+ *
+ * To take full advantage of bulk transmission, packets should be chosen
+ * between 1k and 5k in size (1k to make sure the sending side starts
+ * straming, and <5k to avoid overflowing the system with small TDs).
+ */
+
+
+/* probe/attach/detach:
+ * Connect the driver to the hardware and netgraph
+ *
+ * udbp_setup_out_transfer(sc);
+ * Setup an outbound transfer. Only one transmit can be active at the same
+ * time.
+ * XXX If it is required that the driver is able to queue multiple requests
+ * let me know. That is slightly difficult, due to the fact that we
+ * cannot call usbd_alloc_xfer in int context.
+ *
+ * udbp_setup_in_transfer(sc)
+ * Prepare an in transfer that will be waiting for data to come in. It
+ * is submitted and sits there until data is available.
+ * The callback resubmits a new transfer on completion.
+ *
+ * The reason we submit a bulk in transfer is that USB does not know about
+ * interrupts. The bulk transfer continuously polls the device for data.
+ * While the device has no data available, the device NAKs the TDs. As soon
+ * as there is data, the transfer happens and the data comes flowing in.
+ *
+ * In case you were wondering, interrupt transfers happen exactly that way.
+ * It therefore doesn't make sense to use the interrupt pipe to signal
+ * 'data ready' and then schedule a bulk transfer to fetch it. That would
+ * incur a 2ms delay at least, without reducing bandwidth requirements.
+ *
+ */
+
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbhid.h>
+
+#include "usbdevs.h"
+
+
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_parse.h>
+#include <dev/usb/udbp.h>
+#include <netgraph/netgraph.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (udbpdebug) printf x
+#define DPRINTFN(n,x) if (udbpdebug>(n)) printf x
+int udbpdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp");
+SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW,
+ &udbpdebug, 0, "udbp debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+#define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in msecs */
+#define UDBP_BUFFERSIZE 2048 /* maximum number of bytes in one transfer */
+
+
+struct udbp_softc {
+ device_t sc_dev; /* base device */
+ usbd_interface_handle sc_iface;
+
+ usbd_pipe_handle sc_bulkin_pipe;
+ int sc_bulkin;
+ usbd_xfer_handle sc_bulkin_xfer;
+ void *sc_bulkin_buffer;
+ int sc_bulkin_bufferlen;
+ int sc_bulkin_datalen;
+
+ usbd_pipe_handle sc_bulkout_pipe;
+ int sc_bulkout;
+ usbd_xfer_handle sc_bulkout_xfer;
+ void *sc_bulkout_buffer;
+ int sc_bulkout_bufferlen;
+ int sc_bulkout_datalen;
+
+ int flags;
+# define DISCONNECTED 0x01
+# define OUT_BUSY 0x02
+# define NETGRAPH_INITIALISED 0x04
+ node_p node; /* back pointer to node */
+ hook_p hook; /* pointer to the hook */
+ u_int packets_in; /* packets in from downstream */
+ u_int packets_out; /* packets out towards downstream */
+ struct ifqueue xmitq_hipri; /* hi-priority transmit queue */
+ struct ifqueue xmitq; /* low-priority transmit queue */
+
+};
+typedef struct udbp_softc *udbp_p;
+
+
+
+static ng_constructor_t ng_udbp_constructor;
+static ng_rcvmsg_t ng_udbp_rcvmsg;
+static ng_shutdown_t ng_udbp_rmnode;
+static ng_newhook_t ng_udbp_newhook;
+static ng_connect_t ng_udbp_connect;
+static ng_rcvdata_t ng_udbp_rcvdata;
+static ng_disconnect_t ng_udbp_disconnect;
+
+/* Parse type for struct ngudbpstat */
+static const struct ng_parse_struct_field
+ ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO;
+static const struct ng_parse_type ng_udbp_stat_type = {
+ &ng_parse_struct_type,
+ &ng_udbp_stat_type_fields
+};
+
+/* List of commands and how to convert arguments to/from ASCII */
+static const struct ng_cmdlist ng_udbp_cmdlist[] = {
+ {
+ NGM_UDBP_COOKIE,
+ NGM_UDBP_GET_STATUS,
+ "getstatus",
+ NULL,
+ &ng_udbp_stat_type,
+ },
+ {
+ NGM_UDBP_COOKIE,
+ NGM_UDBP_SET_FLAG,
+ "setflag",
+ &ng_parse_int32_type,
+ NULL
+ },
+ { 0 }
+};
+
+/* Netgraph node type descriptor */
+static struct ng_type ng_udbp_typestruct = {
+ .version = NG_ABI_VERSION,
+ .name = NG_UDBP_NODE_TYPE,
+ .constructor = ng_udbp_constructor,
+ .rcvmsg = ng_udbp_rcvmsg,
+ .shutdown = ng_udbp_rmnode,
+ .newhook = ng_udbp_newhook,
+ .connect = ng_udbp_connect,
+ .rcvdata = ng_udbp_rcvdata,
+ .disconnect = ng_udbp_disconnect,
+ .cmdlist = ng_udbp_cmdlist,
+};
+
+static int udbp_setup_in_transfer (udbp_p sc);
+static void udbp_in_transfer_cb (usbd_xfer_handle xfer,
+ usbd_private_handle priv,
+ usbd_status err);
+
+static int udbp_setup_out_transfer (udbp_p sc);
+static void udbp_out_transfer_cb (usbd_xfer_handle xfer,
+ usbd_private_handle priv,
+ usbd_status err);
+
+static device_probe_t udbp_match;
+static device_attach_t udbp_attach;
+static device_detach_t udbp_detach;
+
+static device_method_t udbp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, udbp_match),
+ DEVMETHOD(device_attach, udbp_attach),
+ DEVMETHOD(device_detach, udbp_detach),
+
+ { 0, 0 }
+};
+
+static driver_t udbp_driver = {
+ "udbp",
+ udbp_methods,
+ sizeof(struct udbp_softc)
+};
+
+static devclass_t udbp_devclass;
+
+static int
+udbp_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+
+ /* XXX Julian, add the id of the device if you have one to test
+ * things with. run 'usbdevs -v' and note the 3 ID's that appear.
+ * The Vendor Id and Product Id are in hex and the Revision Id is in
+ * bcd. But as usual if the revision is 0x101 then you should compare
+ * the revision id in the device descriptor with 0x101
+ * Or go search the file usbdevs.h. Maybe the device is already in
+ * there.
+ */
+ if ((uaa->vendor == USB_VENDOR_NETCHIP &&
+ uaa->product == USB_PRODUCT_NETCHIP_TURBOCONNECT))
+ return (UMATCH_VENDOR_PRODUCT);
+
+ if ((uaa->vendor == USB_VENDOR_PROLIFIC &&
+ (uaa->product == USB_PRODUCT_PROLIFIC_PL2301 ||
+ uaa->product == USB_PRODUCT_PROLIFIC_PL2302)))
+ return (UMATCH_VENDOR_PRODUCT);
+
+ if ((uaa->vendor == USB_VENDOR_ANCHOR &&
+ uaa->product == USB_PRODUCT_ANCHOR_EZLINK))
+ return (UMATCH_VENDOR_PRODUCT);
+
+ if ((uaa->vendor == USB_VENDOR_GENESYS &&
+ uaa->product == USB_PRODUCT_GENESYS_GL620USB))
+ return (UMATCH_VENDOR_PRODUCT);
+
+ return (UMATCH_NONE);
+}
+
+static int
+udbp_attach(device_t self)
+{
+ struct udbp_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed, *ed_bulkin = NULL, *ed_bulkout = NULL;
+ usbd_status err;
+ int i;
+ static int ngudbp_done_init=0;
+
+ sc->flags |= DISCONNECTED;
+ /* fetch the interface handle for the first interface */
+ (void) usbd_device2interface_handle(uaa->device, 0, &iface);
+ id = usbd_get_interface_descriptor(iface);
+ sc->sc_dev = self;
+
+ /* Find the two first bulk endpoints */
+ for (i = 0 ; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (!ed) {
+ device_printf(self, "could not read endpoint descriptor\n");
+ return ENXIO;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkin = ed;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkout = ed;
+ }
+
+ if (ed_bulkin && ed_bulkout) /* found all we need */
+ break;
+ }
+
+ /* Verify that we goething sensible */
+ if (ed_bulkin == NULL || ed_bulkout == NULL) {
+ device_printf(self, "bulk-in and/or bulk-out endpoint not found\n");
+ return ENXIO;
+ }
+
+ if (ed_bulkin->wMaxPacketSize[0] != ed_bulkout->wMaxPacketSize[0] ||
+ ed_bulkin->wMaxPacketSize[1] != ed_bulkout->wMaxPacketSize[1]) {
+ device_printf(self,
+ "bulk-in and bulk-out have different packet sizes %d %d %d %d\n",
+ ed_bulkin->wMaxPacketSize[0],
+ ed_bulkout->wMaxPacketSize[0],
+ ed_bulkin->wMaxPacketSize[1],
+ ed_bulkout->wMaxPacketSize[1]);
+ return ENXIO;
+ }
+
+ sc->sc_bulkin = ed_bulkin->bEndpointAddress;
+ sc->sc_bulkout = ed_bulkout->bEndpointAddress;
+
+ DPRINTF(("%s: Bulk-in: 0x%02x, bulk-out 0x%02x, packet size = %d\n",
+ device_get_nameunit(sc->sc_dev), sc->sc_bulkin, sc->sc_bulkout,
+ ed_bulkin->wMaxPacketSize[0]));
+
+ /* Allocate the in transfer struct */
+ sc->sc_bulkin_xfer = usbd_alloc_xfer(uaa->device);
+ if (!sc->sc_bulkin_xfer) {
+ goto bad;
+ }
+ sc->sc_bulkout_xfer = usbd_alloc_xfer(uaa->device);
+ if (!sc->sc_bulkout_xfer) {
+ goto bad;
+ }
+ sc->sc_bulkin_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ if (!sc->sc_bulkin_buffer) {
+ goto bad;
+ }
+ sc->sc_bulkout_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ if (!sc->sc_bulkout_xfer || !sc->sc_bulkout_buffer) {
+ goto bad;
+ }
+ sc->sc_bulkin_bufferlen = UDBP_BUFFERSIZE;
+ sc->sc_bulkout_bufferlen = UDBP_BUFFERSIZE;
+
+ /* We have decided on which endpoints to use, now open the pipes */
+ err = usbd_open_pipe(iface, sc->sc_bulkin,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
+ if (err) {
+ device_printf(self, "cannot open bulk-in pipe (addr %d)\n",
+ sc->sc_bulkin);
+ goto bad;
+ }
+ err = usbd_open_pipe(iface, sc->sc_bulkout,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ device_printf(self, "cannot open bulk-out pipe (addr %d)\n",
+ sc->sc_bulkout);
+ goto bad;
+ }
+
+ if (!ngudbp_done_init){
+ ngudbp_done_init=1;
+ if (ng_newtype(&ng_udbp_typestruct)) {
+ printf("ngudbp install failed\n");
+ goto bad;
+ }
+ }
+
+ if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) {
+ char nodename[128];
+ sprintf(nodename, "%s", device_get_nameunit(sc->sc_dev));
+ if ((err = ng_name_node(sc->node, nodename))) {
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL;
+ goto bad;
+ } else {
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ sc->xmitq.ifq_maxlen = IFQ_MAXLEN;
+ sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN;
+ mtx_init(&sc->xmitq.ifq_mtx, "usb_xmitq", NULL,
+ MTX_DEF);
+ mtx_init(&sc->xmitq_hipri.ifq_mtx,
+ "usb_xmitq_hipri", NULL, MTX_DEF);
+ }
+ }
+ sc->flags = NETGRAPH_INITIALISED;
+ /* sc->flags &= ~DISCONNECTED; */ /* XXX */
+
+
+ /* the device is now operational */
+
+
+ /* schedule the first incoming xfer */
+ err = udbp_setup_in_transfer(sc);
+ if (err) {
+ goto bad;
+ }
+ return 0;
+bad:
+#if 0 /* probably done in udbp_detach() */
+ if (sc->sc_bulkout_buffer) {
+ free(sc->sc_bulkout_buffer, M_USBDEV);
+ }
+ if (sc->sc_bulkin_buffer) {
+ free(sc->sc_bulkin_buffer, M_USBDEV);
+ }
+ if (sc->sc_bulkout_xfer) {
+ usbd_free_xfer(sc->sc_bulkout_xfer);
+ }
+ if (sc->sc_bulkin_xfer) {
+ usbd_free_xfer(sc->sc_bulkin_xfer);
+ }
+#endif
+ udbp_detach(self);
+ return ENXIO;
+}
+
+
+static int
+udbp_detach(device_t self)
+{
+ struct udbp_softc *sc = device_get_softc(self);
+
+ sc->flags |= DISCONNECTED;
+
+ DPRINTF(("%s: disconnected\n", device_get_nameunit(self)));
+
+ if (sc->sc_bulkin_pipe) {
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ }
+ if (sc->sc_bulkout_pipe) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ }
+
+ if (sc->flags & NETGRAPH_INITIALISED) {
+ ng_rmnode_self(sc->node);
+ NG_NODE_SET_PRIVATE(sc->node, NULL);
+ NG_NODE_UNREF(sc->node);
+ sc->node = NULL; /* Paranoid */
+ }
+
+ if (sc->sc_bulkin_xfer)
+ usbd_free_xfer(sc->sc_bulkin_xfer);
+ if (sc->sc_bulkout_xfer)
+ usbd_free_xfer(sc->sc_bulkout_xfer);
+
+ if (sc->sc_bulkin_buffer)
+ free(sc->sc_bulkin_buffer, M_USBDEV);
+ if (sc->sc_bulkout_buffer)
+ free(sc->sc_bulkout_buffer, M_USBDEV);
+ return 0;
+}
+
+
+static int
+udbp_setup_in_transfer(udbp_p sc)
+{
+ void *priv = sc; /* XXX this should probably be some pointer to
+ * struct describing the transfer (mbuf?)
+ * See also below.
+ */
+ usbd_status err;
+
+ /* XXX
+ * How should we arrange for 2 extra bytes at the start of the
+ * packet?
+ */
+
+ /* Initialise a USB transfer and then schedule it */
+
+ (void) usbd_setup_xfer( sc->sc_bulkin_xfer,
+ sc->sc_bulkin_pipe,
+ priv,
+ sc->sc_bulkin_buffer,
+ sc->sc_bulkin_bufferlen,
+ USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT,
+ udbp_in_transfer_cb);
+
+ err = usbd_transfer(sc->sc_bulkin_xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(("%s: failed to setup in-transfer, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err)));
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static void
+udbp_in_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ udbp_p sc = priv; /* XXX see priv above */
+ int s;
+ int len;
+ struct mbuf *m;
+
+ if (err) {
+ if (err != USBD_CANCELLED) {
+ DPRINTF(("%s: bulk-out transfer failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err)));
+ } else {
+ /* USBD_CANCELLED happens at unload of the driver */
+ return;
+ }
+
+ /* Transfer has failed, packet is not received */
+ } else {
+
+ len = xfer->actlen;
+
+ s = splimp(); /* block network stuff too */
+ if (sc->hook) {
+ /* get packet from device and send on */
+ m = m_devget(sc->sc_bulkin_buffer, len, 0, NULL, NULL);
+ NG_SEND_DATA_ONLY(err, sc->hook, m);
+ }
+ splx(s);
+
+ }
+ /* schedule the next in transfer */
+ udbp_setup_in_transfer(sc);
+}
+
+
+static int
+udbp_setup_out_transfer(udbp_p sc)
+{
+ void *priv = sc; /* XXX this should probably be some pointer to
+ * struct describing the transfer (mbuf?)
+ * See also below.
+ */
+ int pktlen;
+ usbd_status err;
+ int s, s1;
+ struct mbuf *m;
+
+
+ s = splusb();
+ if (sc->flags & OUT_BUSY)
+ panic("out transfer already in use, we should add queuing");
+ sc->flags |= OUT_BUSY;
+ splx(s);
+ s1 = splimp(); /* Queueing happens at splnet */
+ IF_DEQUEUE(&sc->xmitq_hipri, m);
+ if (m == NULL) {
+ IF_DEQUEUE(&sc->xmitq, m);
+ }
+ splx(s1);
+
+ if (!m) {
+ sc->flags &= ~OUT_BUSY;
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ pktlen = m->m_pkthdr.len;
+ if (pktlen > sc->sc_bulkout_bufferlen) {
+ device_printf(sc->sc_dev, "Packet too large, %d > %d\n",
+ pktlen, sc->sc_bulkout_bufferlen);
+ return (USBD_IOERROR);
+ }
+
+ m_copydata(m, 0, pktlen, sc->sc_bulkout_buffer);
+ m_freem(m);
+
+ /* Initialise a USB transfer and then schedule it */
+
+ (void) usbd_setup_xfer( sc->sc_bulkout_xfer,
+ sc->sc_bulkout_pipe,
+ priv,
+ sc->sc_bulkout_buffer,
+ pktlen,
+ USBD_SHORT_XFER_OK,
+ UDBP_TIMEOUT,
+ udbp_out_transfer_cb);
+
+ err = usbd_transfer(sc->sc_bulkout_xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(("%s: failed to setup out-transfer, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err)));
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static void
+udbp_out_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ udbp_p sc = priv; /* XXX see priv above */
+ int s;
+
+ if (err) {
+ DPRINTF(("%s: bulk-out transfer failed: %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err)));
+ /* Transfer has failed, packet is not transmitted */
+ /* XXX Invalidate packet */
+ return;
+ }
+
+ /* packet has been transmitted */
+
+ s = splusb(); /* mark the buffer available */
+ sc->flags &= ~OUT_BUSY;
+ udbp_setup_out_transfer(sc);
+ splx(s);
+}
+
+DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
+MODULE_DEPEND(udbp, usb, 1, 1, 1);
+
+
+/***********************************************************************
+ * Start of Netgraph methods
+ **********************************************************************/
+
+/*
+ * If this is a device node so this work is done in the attach()
+ * routine and the constructor will return EINVAL as you should not be able
+ * to create nodes that depend on hardware (unless you can add the hardware :)
+ */
+static int
+ng_udbp_constructor(node_p node)
+{
+ return (EINVAL);
+}
+
+/*
+ * Give our ok for a hook to be added...
+ * If we are not running this might kick a device into life.
+ * Possibly decode information out of the hook name.
+ * Add the hook's private info to the hook structure.
+ * (if we had some). In this example, we assume that there is a
+ * an array of structs, called 'channel' in the private info,
+ * one for each active channel. The private
+ * pointer of each hook points to the appropriate UDBP_hookinfo struct
+ * so that the source of an input packet is easily identified.
+ */
+static int
+ng_udbp_newhook(node_p node, hook_p hook, const char *name)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(node);
+
+#if 0
+ /* Possibly start up the device if it's not already going */
+ if ((sc->flags & SCF_RUNNING) == 0) {
+ ng_udbp_start_hardware(sc);
+ }
+#endif
+
+ if (strcmp(name, NG_UDBP_HOOK_NAME) == 0) {
+ sc->hook = hook;
+ NG_HOOK_SET_PRIVATE(hook, NULL);
+ } else {
+ return (EINVAL); /* not a hook we know about */
+ }
+ return(0);
+}
+
+/*
+ * Get a netgraph control message.
+ * Check it is one we understand. If needed, send a response.
+ * We could save the address for an async action later, but don't here.
+ * Always free the message.
+ * The response should be in a malloc'd region that the caller can 'free'.
+ * A response is not required.
+ * Theoretically you could respond defferently to old message types if
+ * the cookie in the header didn't match what we consider to be current
+ * (so that old userland programs could continue to work).
+ */
+static int
+ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(node);
+ struct ng_mesg *resp = NULL;
+ int error = 0;
+ struct ng_mesg *msg;
+
+ NGI_GET_MSG(item, msg);
+ /* Deal with message according to cookie and command */
+ switch (msg->header.typecookie) {
+ case NGM_UDBP_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_UDBP_GET_STATUS:
+ {
+ struct ngudbpstat *stats;
+
+ NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
+ if (!resp) {
+ error = ENOMEM;
+ break;
+ }
+ stats = (struct ngudbpstat *) resp->data;
+ stats->packets_in = sc->packets_in;
+ stats->packets_out = sc->packets_out;
+ break;
+ }
+ case NGM_UDBP_SET_FLAG:
+ if (msg->header.arglen != sizeof(u_int32_t)) {
+ error = EINVAL;
+ break;
+ }
+ sc->flags = *((u_int32_t *) msg->data);
+ break;
+ default:
+ error = EINVAL; /* unknown command */
+ break;
+ }
+ break;
+ default:
+ error = EINVAL; /* unknown cookie type */
+ break;
+ }
+
+ /* Take care of synchronous response, if any */
+ NG_RESPOND_MSG(error, node, item, resp);
+ NG_FREE_MSG(msg);
+ return(error);
+}
+
+/*
+ * Accept data from the hook and queue it for output.
+ */
+static int
+ng_udbp_rcvdata(hook_p hook, item_p item)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ int error;
+ struct ifqueue *xmitq_p;
+ int s;
+ struct mbuf *m;
+ struct ng_tag_prio *ptag;
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ /*
+ * Now queue the data for when it can be sent
+ */
+ if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE,
+ NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) )
+ xmitq_p = (&sc->xmitq_hipri);
+ else
+ xmitq_p = (&sc->xmitq);
+
+ s = splusb();
+ IF_LOCK(xmitq_p);
+ if (_IF_QFULL(xmitq_p)) {
+ _IF_DROP(xmitq_p);
+ IF_UNLOCK(xmitq_p);
+ splx(s);
+ error = ENOBUFS;
+ goto bad;
+ }
+ _IF_ENQUEUE(xmitq_p, m);
+ IF_UNLOCK(xmitq_p);
+ if (!(sc->flags & OUT_BUSY))
+ udbp_setup_out_transfer(sc);
+ splx(s);
+ return (0);
+
+bad: /*
+ * It was an error case.
+ * check if we need to free the mbuf, and then return the error
+ */
+ NG_FREE_M(m);
+ return (error);
+}
+
+/*
+ * Do local shutdown processing..
+ * We are a persistant device, we refuse to go away, and
+ * only remove our links and reset ourself.
+ */
+static int
+ng_udbp_rmnode(node_p node)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(node);
+ int err;
+
+ if (sc->flags & DISCONNECTED) {
+ /*
+ * WE are really going away.. hardware must have gone.
+ * Assume that the hardware drive part will clear up the
+ * sc, in fact it may already have done so..
+ * In which case we may have just segfaulted..XXX
+ */
+ return (0);
+ }
+
+ /* stolen from attach routine */
+ /* Drain the queues */
+ IF_DRAIN(&sc->xmitq_hipri);
+ IF_DRAIN(&sc->xmitq);
+
+ sc->packets_in = 0; /* reset stats */
+ sc->packets_out = 0;
+ NG_NODE_UNREF(node); /* forget it ever existed */
+
+ if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) {
+ char nodename[128];
+ sprintf(nodename, "%s", device_get_nameunit(sc->sc_dev));
+ if ((err = ng_name_node(sc->node, nodename))) {
+ NG_NODE_UNREF(sc->node); /* out damned spot! */
+ sc->flags &= ~NETGRAPH_INITIALISED;
+ sc->node = NULL;
+ } else {
+ NG_NODE_SET_PRIVATE(sc->node, sc);
+ }
+ }
+ return (err);
+}
+
+/*
+ * This is called once we've already connected a new hook to the other node.
+ * It gives us a chance to balk at the last minute.
+ */
+static int
+ng_udbp_connect(hook_p hook)
+{
+ /* probably not at splnet, force outward queueing */
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+ /* be really amiable and just say "YUP that's OK by me! " */
+ return (0);
+}
+
+/*
+ * Dook disconnection
+ *
+ * For this type, removal of the last link destroys the node
+ */
+static int
+ng_udbp_disconnect(hook_p hook)
+{
+ const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ sc->hook = NULL;
+
+ if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
+ && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
+ ng_rmnode_self(NG_HOOK_NODE(hook));
+ return (0);
+}
+
diff --git a/sys/legacy/dev/usb/udbp.h b/sys/legacy/dev/usb/udbp.h
new file mode 100644
index 0000000..97ef945
--- /dev/null
+++ b/sys/legacy/dev/usb/udbp.h
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file was derived from src/sys/netgraph/ng_sample.h, revision 1.1
+ * written by Julian Elischer, Whistle Communications.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_UDBP_H_
+#define _NETGRAPH_UDBP_H_
+
+/* Node type name. This should be unique among all netgraph node types */
+#define NG_UDBP_NODE_TYPE "udbp"
+
+/* Node type cookie. Should also be unique. This value MUST change whenever
+ an incompatible change is made to this header file, to insure consistency.
+ The de facto method for generating cookies is to take the output of the
+ date command: date -u +'%s' */
+#define NGM_UDBP_COOKIE 944609300
+
+
+#define NG_UDBP_HOOK_NAME "data"
+
+/* Netgraph commands understood by this node type */
+enum {
+ NGM_UDBP_SET_FLAG = 1,
+ NGM_UDBP_GET_STATUS,
+};
+
+/* This structure is returned by the NGM_UDBP_GET_STATUS command */
+struct ngudbpstat {
+ u_int packets_in; /* packets in from downstream */
+ u_int packets_out; /* packets out towards downstream */
+};
+
+/*
+ * This is used to define the 'parse type' for a struct ngudbpstat, which
+ * is bascially a description of how to convert a binary struct ngudbpstat
+ * to an ASCII string and back. See ng_parse.h for more info.
+ *
+ * This needs to be kept in sync with the above structure definition
+ */
+#define NG_UDBP_STATS_TYPE_INFO { \
+ { "packets_in", &ng_parse_int32_type }, \
+ { "packets_out", &ng_parse_int32_type }, \
+ { NULL }, \
+}
+
+#endif /* _NETGRAPH_UDBP_H_ */
diff --git a/sys/legacy/dev/usb/ufm.c b/sys/legacy/dev/usb/ufm.c
new file mode 100644
index 0000000..2635827
--- /dev/null
+++ b/sys/legacy/dev/usb/ufm.c
@@ -0,0 +1,376 @@
+/*-
+ * Copyright (c) 2001-2007 M. Warner Losh
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include "usbdevs.h"
+#include <dev/usb/dsbr100io.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ufmdebug) printf x
+#define DPRINTFN(n,x) if (ufmdebug>(n)) printf x
+int ufmdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ufm, CTLFLAG_RW, 0, "USB ufm");
+SYSCTL_INT(_hw_usb_ufm, OID_AUTO, debug, CTLFLAG_RW,
+ &ufmdebug, 0, "ufm debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+d_open_t ufmopen;
+d_close_t ufmclose;
+d_ioctl_t ufmioctl;
+
+static struct cdevsw ufm_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ufmopen,
+ .d_close = ufmclose,
+ .d_ioctl = ufmioctl,
+ .d_name = "ufm",
+};
+
+#define FM_CMD0 0x00
+#define FM_CMD_SET_FREQ 0x01
+#define FM_CMD2 0x02
+
+struct ufm_softc {
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+
+ int sc_opened;
+ int sc_epaddr;
+ int sc_freq;
+
+ int sc_refcnt;
+};
+
+#define UFMUNIT(n) (dev2unit(n))
+
+static device_probe_t ufm_match;
+static device_attach_t ufm_attach;
+static device_detach_t ufm_detach;
+
+static device_method_t ufm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ufm_match),
+ DEVMETHOD(device_attach, ufm_attach),
+ DEVMETHOD(device_detach, ufm_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ufm_driver = {
+ "ufm",
+ ufm_methods,
+ sizeof(struct ufm_softc)
+};
+
+static devclass_t ufm_devclass;
+
+static int
+ufm_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_device_descriptor_t *dd;
+
+ DPRINTFN(10,("ufm_match\n"));
+ if (!uaa->iface)
+ return UMATCH_NONE;
+
+ dd = usbd_get_device_descriptor(uaa->device);
+
+ if (dd &&
+ ((UGETW(dd->idVendor) == USB_VENDOR_CYPRESS &&
+ UGETW(dd->idProduct) == USB_PRODUCT_CYPRESS_FMRADIO)))
+ return UMATCH_VENDOR_PRODUCT;
+ else
+ return UMATCH_NONE;
+}
+
+static int
+ufm_attach(device_t self)
+{
+ struct ufm_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_endpoint_descriptor_t *edesc;
+ usbd_device_handle udev;
+ usbd_interface_handle iface;
+ u_int8_t epcount;
+ usbd_status r;
+ char * ermsg = "<none>";
+
+ DPRINTFN(10,("ufm_attach: sc=%p\n", sc));
+ sc->sc_dev = self;
+ sc->sc_udev = udev = uaa->device;
+
+ if ((!uaa->device) || (!uaa->iface)) {
+ ermsg = "device or iface";
+ goto nobulk;
+ }
+ sc->sc_iface = iface = uaa->iface;
+ sc->sc_opened = 0;
+ sc->sc_refcnt = 0;
+
+ r = usbd_endpoint_count(iface, &epcount);
+ if (r != USBD_NORMAL_COMPLETION) {
+ ermsg = "endpoints";
+ goto nobulk;
+ }
+
+ edesc = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!edesc) {
+ ermsg = "interface endpoint";
+ goto nobulk;
+ }
+ sc->sc_epaddr = edesc->bEndpointAddress;
+
+ /* XXX no error trapping, no storing of struct cdev **/
+ (void) make_dev(&ufm_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "ufm%d", device_get_unit(self));
+ DPRINTFN(10, ("ufm_attach: %p\n", sc->sc_udev));
+ return 0;
+
+ nobulk:
+ device_printf(sc->sc_dev, "could not find %s\n", ermsg);
+ return ENXIO;
+}
+
+
+int
+ufmopen(struct cdev *dev, int flag, int mode, struct thread *td)
+{
+ struct ufm_softc *sc;
+
+ int unit = UFMUNIT(dev);
+ sc = devclass_get_softc(ufm_devclass, unit);
+ if (sc == NULL)
+ return (ENXIO);
+
+ DPRINTFN(5, ("ufmopen: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, unit));
+
+ if (sc->sc_opened)
+ return (EBUSY);
+
+ if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))
+ return (EACCES);
+
+ sc->sc_opened = 1;
+ return (0);
+}
+
+int
+ufmclose(struct cdev *dev, int flag, int mode, struct thread *td)
+{
+ struct ufm_softc *sc;
+
+ int unit = UFMUNIT(dev);
+ sc = devclass_get_softc(ufm_devclass, unit);
+
+ DPRINTFN(5, ("ufmclose: flag=%d, mode=%d, unit=%d\n", flag, mode, unit));
+ sc->sc_opened = 0;
+ sc->sc_refcnt = 0;
+ return 0;
+}
+
+static int
+ufm_do_req(struct ufm_softc *sc, u_int8_t reqtype, u_int8_t request,
+ u_int16_t value, u_int16_t index, u_int8_t len, void *retbuf)
+{
+ int s;
+ usb_device_request_t req;
+ usbd_status err;
+
+ s = splusb();
+ req.bmRequestType = reqtype;
+ req.bRequest = request;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, len);
+ err = usbd_do_request_flags(sc->sc_udev, &req, retbuf, 0, NULL,
+ USBD_DEFAULT_TIMEOUT);
+ splx(s);
+ if (err)
+ return (EIO);
+ return (0);
+}
+
+static int
+ufm_set_freq(struct ufm_softc *sc, caddr_t addr)
+{
+ int freq = *(int *)addr;
+ u_int8_t ret;
+
+ /*
+ * Freq now is in Hz. We need to convert it to the frequency
+ * that the radio wants. This frequency is 10.7MHz above
+ * the actual frequency. We then need to convert to
+ * units of 12.5kHz. We add one to the IFM to make rounding
+ * easier.
+ */
+ sc->sc_freq = freq;
+ freq = (freq + 10700001) / 12500;
+ /* This appears to set the frequency */
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD_SET_FREQ, freq >> 8,
+ freq, 1, &ret) != 0)
+ return (EIO);
+ /* Not sure what this does */
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x96, 0xb7, 1,
+ &ret) != 0)
+ return (EIO);
+ return (0);
+}
+
+static int
+ufm_get_freq(struct ufm_softc *sc, caddr_t addr)
+{
+ int *valp = (int *)addr;
+ *valp = sc->sc_freq;
+ return (0);
+}
+
+static int
+ufm_start(struct ufm_softc *sc, caddr_t addr)
+{
+ u_int8_t ret;
+
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x00, 0xc7,
+ 1, &ret))
+ return (EIO);
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD2, 0x01, 0x00,
+ 1, &ret))
+ return (EIO);
+ if (ret & 0x1)
+ return (EIO);
+ return (0);
+}
+
+static int
+ufm_stop(struct ufm_softc *sc, caddr_t addr)
+{
+ u_int8_t ret;
+
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x16, 0x1C,
+ 1, &ret))
+ return (EIO);
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD2, 0x00, 0x00,
+ 1, &ret))
+ return (EIO);
+ return (0);
+}
+
+static int
+ufm_get_stat(struct ufm_softc *sc, caddr_t addr)
+{
+ u_int8_t ret;
+
+ /*
+ * Note, there's a 240ms settle time before the status
+ * will be valid, so tsleep that amount. hz/4 is a good
+ * approximation of that. Since this is a short sleep
+ * we don't try to catch any signals to keep things
+ * simple.
+ */
+ tsleep(sc, 0, "ufmwait", hz/4);
+ if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x00, 0x24,
+ 1, &ret))
+ return (EIO);
+ *(int *)addr = ret;
+
+ return (0);
+}
+
+int
+ufmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
+{
+ struct ufm_softc *sc;
+
+ int unit = UFMUNIT(dev);
+ int error = 0;
+
+ sc = devclass_get_softc(ufm_devclass, unit);
+
+ switch (cmd) {
+ case FM_SET_FREQ:
+ error = ufm_set_freq(sc, addr);
+ break;
+ case FM_GET_FREQ:
+ error = ufm_get_freq(sc, addr);
+ break;
+ case FM_START:
+ error = ufm_start(sc, addr);
+ break;
+ case FM_STOP:
+ error = ufm_stop(sc, addr);
+ break;
+ case FM_GET_STAT:
+ error = ufm_get_stat(sc, addr);
+ break;
+ default:
+ return ENOTTY;
+ break;
+ }
+ return error;
+}
+
+static int
+ufm_detach(device_t self)
+{
+ return 0;
+}
+
+MODULE_DEPEND(ufm, usb, 1, 1, 1);
+DRIVER_MODULE(ufm, uhub, ufm_driver, ufm_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/ufoma.c b/sys/legacy/dev/usb/ufoma.c
new file mode 100644
index 0000000..e5704a7
--- /dev/null
+++ b/sys/legacy/dev/usb/ufoma.c
@@ -0,0 +1,1192 @@
+/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
+#define UFOMA_HANDSFREE
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2005, Takanori Watanabe
+ * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*-
+ * 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.
+ */
+
+/*
+ * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+/*
+ * TODO:
+ * - Add error recovery in various places; the big problem is what
+ * to do in a callback if there is an error.
+ * - Implement a Call Device for modems without multiplexed commands.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/serial.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/sbuf.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+#include <sys/taskqueue.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+#include "usbdevs.h"
+
+typedef struct ufoma_mobile_acm_descriptor{
+ uByte bFunctionLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bType;
+ uByte bMode[1];
+}usb_mcpc_acm_descriptor;
+
+#define UISUBCLASS_MCPC 0x88
+
+#define UDESC_VS_INTERFACE 0x44
+#define UDESCSUB_MCPC_ACM 0x11
+
+#define UMCPC_ACM_TYPE_AB1 0x1
+#define UMCPC_ACM_TYPE_AB2 0x2
+#define UMCPC_ACM_TYPE_AB5 0x5
+#define UMCPC_ACM_TYPE_AB6 0x6
+
+#define UMCPC_ACM_MODE_DEACTIVATED 0x0
+#define UMCPC_ACM_MODE_MODEM 0x1
+#define UMCPC_ACM_MODE_ATCOMMAND 0x2
+#define UMCPC_ACM_MODE_OBEX 0x60
+#define UMCPC_ACM_MODE_VENDOR1 0xc0
+#define UMCPC_ACM_MODE_VENDOR2 0xfe
+#define UMCPC_ACM_MODE_UNLINKED 0xff
+
+#define UMCPC_CM_MOBILE_ACM 0x0
+
+#define UMCPC_ACTIVATE_MODE 0x60
+#define UMCPC_GET_MODETABLE 0x61
+#define UMCPC_SET_LINK 0x62
+#define UMCPC_CLEAR_LINK 0x63
+
+#define UMCPC_REQUEST_ACKNOLEDGE 0x31
+
+#define UFOMA_MAX_TIMEOUT 15 /*Standard says 10(sec)*/
+#define UFOMA_CMD_BUF_SIZE 64
+
+#define UMODEMIBUFSIZE 1024
+#define UMODEMOBUFSIZE 1024
+#define DPRINTF(a)
+
+struct ufoma_softc{
+ struct ucom_softc sc_ucom;
+ int sc_is_ucom;
+ int sc_isopen;
+
+ struct mtx sc_mtx;
+ int sc_ctl_iface_no;
+ usbd_interface_handle sc_ctl_iface;
+ usbd_interface_handle sc_data_iface;
+ int sc_data_iface_no;
+ int sc_cm_cap;
+ int sc_acm_cap;
+ usb_cdc_line_state_t sc_line_state; /* current line state */
+ usb_cdc_line_state_t sc_line_state_init; /* pre open line state*/
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+
+ usbd_pipe_handle sc_notify_pipe;
+ usb_cdc_notification_t sc_notify_buf;
+ u_char sc_lsr;
+ u_char sc_msr;
+
+ struct task sc_task;
+ uByte *sc_modetable;
+ uByte sc_modetoactivate;
+ uByte sc_currentmode;
+ char sc_resbuffer[UFOMA_CMD_BUF_SIZE+1];
+ int sc_cmdbp;
+ int sc_nummsg;
+ usbd_xfer_handle sc_msgxf;
+};
+static usbd_status
+ufoma_set_line_coding(struct ufoma_softc *sc, usb_cdc_line_state_t *state);
+static device_probe_t ufoma_match;
+static device_attach_t ufoma_attach;
+static device_detach_t ufoma_detach;
+static void *ufoma_get_intconf(usb_config_descriptor_t *cd, usb_interface_descriptor_t *id,int type, int subtype);
+static void ufoma_notify(void * ,int count);
+static void ufoma_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static char *ufoma_mode_to_str(int);
+static int ufoma_str_to_mode(char *);
+
+#ifdef UFOMA_HANDSFREE
+/*Pseudo ucom stuff(for Handsfree interface)*/
+static int ufoma_init_pseudo_ucom(struct ufoma_softc *);
+static tsw_open_t ufoma_open;
+static tsw_close_t ufoma_close;
+static tsw_outwakeup_t ufoma_outwakeup;
+static tsw_free_t ufoma_free;
+#endif
+
+/*umodem like stuff*/
+static int ufoma_init_modem(struct ufoma_softc *, struct usb_attach_arg *);
+static void ufoma_get_status(void *, int portno, u_char *lst, u_char *msr);
+static void ufoma_set(void *, int portno, int reg, int onoff);
+static int ufoma_param(void *, int portno, struct termios *);
+static int ufoma_ucom_open(void *, int portno);
+static void ufoma_ucom_close(void *, int portno);
+static void ufoma_break(struct ufoma_softc *sc, int onoff);
+static void ufoma_dtr(struct ufoma_softc *sc, int onoff);
+static void ufoma_rts(struct ufoma_softc *sc, int onoff);
+
+/*sysctl stuff*/
+static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS);
+static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS);
+static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS);
+static void ufoma_set_line_state(struct ufoma_softc *sc);
+
+static struct ucom_callback ufoma_callback = {
+ .ucom_get_status = ufoma_get_status,
+ .ucom_set = ufoma_set,
+ .ucom_param = ufoma_param,
+ .ucom_open = ufoma_ucom_open,
+ .ucom_close = ufoma_ucom_close,
+};
+
+
+static device_method_t ufoma_methods[] = {
+ /**/
+ DEVMETHOD(device_probe, ufoma_match),
+ DEVMETHOD(device_attach, ufoma_attach),
+ DEVMETHOD(device_detach, ufoma_detach),
+ {0, 0}
+};
+struct umcpc_modetostr_tab{
+ int mode;
+ char *str;
+}umcpc_modetostr_tab[]={
+ {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"},
+ {UMCPC_ACM_MODE_MODEM, "modem"},
+ {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"},
+ {UMCPC_ACM_MODE_OBEX, "obex"},
+ {UMCPC_ACM_MODE_VENDOR1, "vendor1"},
+ {UMCPC_ACM_MODE_VENDOR2, "vendor2"},
+ {UMCPC_ACM_MODE_UNLINKED, "unlinked"},
+ {0, NULL}
+};
+
+static driver_t ufoma_driver = {
+ "ucom",
+ ufoma_methods,
+ sizeof(struct ufoma_softc)
+};
+
+
+DRIVER_MODULE(ufoma, uhub, ufoma_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(ufoma, usb, 1, 1, 1);
+MODULE_DEPEND(ufoma, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+
+static int
+ufoma_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+ usb_config_descriptor_t *cd;
+ usb_mcpc_acm_descriptor *mad;
+ int ret;
+
+ ret = UMATCH_NONE;
+
+ if(uaa->iface == NULL)
+ return(UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ cd = usbd_get_config_descriptor(uaa->device);
+
+ if(id == NULL || cd == NULL)
+ return (UMATCH_NONE);
+
+ if( id->bInterfaceClass == UICLASS_CDC &&
+ id->bInterfaceSubClass == UISUBCLASS_MCPC){
+ ret = (UMATCH_IFACECLASS_IFACESUBCLASS);
+ }else{
+ return UMATCH_NONE;
+ }
+
+ mad = ufoma_get_intconf(cd, id , UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
+ if(mad == NULL){
+ return (UMATCH_NONE);
+ }
+
+#ifndef UFOMA_HANDSFREE
+ if((mad->bType == UMCPC_ACM_TYPE_AB5)||
+ (mad->bType == UMCPC_ACM_TYPE_AB6)){
+ return UMATCH_NONE;
+ }
+#endif
+ return ret;
+}
+
+static int
+ufoma_attach(device_t self)
+{
+ struct ufoma_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usb_config_descriptor_t *cd;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usb_mcpc_acm_descriptor *mad;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ const char *devname,*modename;
+ int ctl_notify;
+ int i,err;
+ int elements;
+ uByte *mode;
+ struct sysctl_ctx_list *sctx;
+ struct sysctl_oid *soid;
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ sc->sc_ctl_iface = uaa->iface;
+ mtx_init(&sc->sc_mtx, "ufoma", NULL, MTX_DEF);
+
+ cd = usbd_get_config_descriptor(ucom->sc_udev);
+ id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
+ sc->sc_ctl_iface_no = id->bInterfaceNumber;
+
+ devname = device_get_nameunit(self);
+ device_printf(self, "iclass %d/%d ifno:%d\n",
+ id->bInterfaceClass, id->bInterfaceSubClass, sc->sc_ctl_iface_no);
+
+ ctl_notify = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
+ if (ed == NULL)
+ continue;
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
+ ctl_notify = ed->bEndpointAddress;
+ }
+ }
+
+ if(ctl_notify== -1){
+ /*NOTIFY is mandatory.*/
+ printf("NOTIFY interface not found\n");
+ goto error;
+ }
+
+ err = usbd_open_pipe_intr(sc->sc_ctl_iface, ctl_notify,
+ USBD_SHORT_XFER_OK, &sc->sc_notify_pipe, sc, &sc->sc_notify_buf,
+ sizeof(sc->sc_notify_buf), ufoma_intr, USBD_DEFAULT_INTERVAL);
+ if(err){
+ printf("PIPE open error %d\n", err);
+ goto error;
+ }
+ mad = ufoma_get_intconf(cd, id , UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
+ if(mad ==NULL){
+ goto error;
+ }
+
+ printf("%s:Supported Mode:", devname);
+ for(mode = mad->bMode;
+ mode < ((uByte *)mad + mad->bFunctionLength); mode++){
+ modename = ufoma_mode_to_str(*mode);
+ if(modename){
+ printf("%s", ufoma_mode_to_str(*mode));
+ }else{
+ printf("(%x)", *mode);
+ }
+ if(mode != ((uByte*)mad + mad->bFunctionLength-1)){
+ printf(",");
+ }
+ }
+ printf("\n");
+ if((mad->bType == UMCPC_ACM_TYPE_AB5)
+ ||(mad->bType == UMCPC_ACM_TYPE_AB6)){
+#ifdef UFOMA_HANDSFREE
+ /*These does not have data interface*/
+ sc->sc_is_ucom = 0;
+ ufoma_init_pseudo_ucom(sc);
+#else
+ /*Should not happen*/
+ goto error;
+#endif
+
+ }else{
+ if(ufoma_init_modem(sc, uaa)){
+ goto error;
+ }
+ }
+ elements = mad->bFunctionLength - sizeof(*mad)+1;
+ sc->sc_msgxf = usbd_alloc_xfer(ucom->sc_udev);
+ sc->sc_nummsg = 0;
+
+ /*Initialize Mode vars.*/
+ sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK);
+ sc->sc_modetable[0] = elements + 1;
+ bcopy(mad->bMode, &sc->sc_modetable[1], elements);
+ sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED;
+ sc->sc_modetoactivate = mad->bMode[0];
+
+ /*Sysctls*/
+ sctx = device_get_sysctl_ctx(self);
+ soid = device_get_sysctl_tree(self);
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode",
+ CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support,
+ "A", "Supporting port role");
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode",
+ CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current,
+ "A", "Current port role");
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode",
+ CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open,
+ "A", "Mode to transit when port is opened");
+
+ return 0;
+ error:
+ if(sc->sc_modetable)
+ free(sc->sc_modetable, M_USBDEV);
+ return EIO;
+}
+
+static int
+ufoma_detach(device_t self)
+{
+ struct ufoma_softc *sc = device_get_softc(self);
+ int rv = 0;
+
+ usbd_free_xfer(sc->sc_msgxf);
+ sc->sc_ucom.sc_dying = 1;
+ usbd_abort_pipe(sc->sc_notify_pipe);
+ usbd_close_pipe(sc->sc_notify_pipe);
+ if(sc->sc_is_ucom){
+ ucom_detach(&sc->sc_ucom);
+ }
+#ifdef UFOMA_HANDSFREE
+ else{
+ tty_lock(sc->sc_ucom.sc_tty);
+ tty_rel_gone(sc->sc_ucom.sc_tty);
+ }
+
+#endif
+ free(sc->sc_modetable, M_USBDEV);
+ return rv;
+}
+
+
+static char *ufoma_mode_to_str(int mode)
+{
+ int i;
+ for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
+ if(umcpc_modetostr_tab[i].mode == mode){
+ return umcpc_modetostr_tab[i].str;
+ }
+ }
+ return NULL;
+}
+
+static int ufoma_str_to_mode(char *str)
+{
+ int i;
+ for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
+ if(strcmp(str, umcpc_modetostr_tab[i].str)==0){
+ return umcpc_modetostr_tab[i].mode;
+ }
+ }
+ return -1;
+}
+
+static void *ufoma_get_intconf( usb_config_descriptor_t *cd,
+ usb_interface_descriptor_t *id, int type, int subtype)
+{
+ uByte *p, *end;
+ usb_descriptor_t *ud=NULL;
+ int flag=0;
+
+
+ for(p = (uByte *)cd,end = p + UGETW(cd->wTotalLength); p < end;
+ p += ud->bLength){
+ ud = (usb_descriptor_t *)p;
+ if(flag && ud->bDescriptorType==UDESC_INTERFACE){
+ return NULL;
+ }
+ /*Read through this interface desc.*/
+ if(bcmp(p, id, sizeof(*id))==0){
+ flag=1;
+ continue;
+ }
+ if(flag==0)
+ continue;
+ if(ud->bDescriptorType == type
+ && ud->bDescriptorSubtype == subtype){
+ break;
+ }
+ }
+ return ud;
+}
+
+
+
+static int ufoma_link_state(struct ufoma_softc *sc)
+{
+ usb_device_request_t req;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ int err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
+ req.bRequest = UMCPC_SET_LINK;
+ USETW(req.wValue, UMCPC_CM_MOBILE_ACM);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, sc->sc_modetable[0]);
+
+ err = usbd_do_request(ucom->sc_udev, &req, sc->sc_modetable);
+ if(err){
+ printf("SET_LINK:%s\n",usbd_errstr(err));
+ return EIO;
+ }
+ err = tsleep(&sc->sc_currentmode, PZERO|PCATCH, "fmalnk", hz);
+ if(err){
+ printf("NO response");
+ return EIO;
+ }
+ if(sc->sc_currentmode != UMCPC_ACM_MODE_DEACTIVATED){
+ return EIO;
+ }
+ return 0;
+}
+
+static int ufoma_activate_state(struct ufoma_softc *sc, int state)
+{
+ usb_device_request_t req;
+ int err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+
+ req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
+ req.bRequest = UMCPC_ACTIVATE_MODE;
+ USETW(req.wValue, state);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if(err){
+ printf("%s:ACTIVATE(%x):%s\n",
+ device_get_nameunit(ucom->sc_dev), state,
+ usbd_errstr(err));
+ return EIO;
+ }
+
+ err = tsleep(&sc->sc_currentmode, PZERO|PCATCH, "fmaact", UFOMA_MAX_TIMEOUT*hz);
+ if(err){
+ printf("%s:NO response", device_get_nameunit(ucom->sc_dev));
+ return EIO;
+ }
+ if(sc->sc_currentmode != state){
+ return EIO;
+ }
+ return 0;
+}
+
+#ifdef UFOMA_HANDSFREE
+static inline void ufoma_setup_msg_req(struct ufoma_softc *sc, usb_device_request_t *req)
+{
+ req->bmRequestType = UT_READ_CLASS_INTERFACE;
+ req->bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
+ USETW(req->wIndex, sc->sc_ctl_iface_no);
+ USETW(req->wValue, 0);
+ USETW(req->wLength, UFOMA_CMD_BUF_SIZE);
+}
+
+
+static void ufoma_msg(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ usb_device_request_t req;
+ struct ufoma_softc *sc = priv;
+ int actlen,i;
+ struct ucom_softc *ucom= &sc->sc_ucom;
+ usbd_get_xfer_status(xfer, NULL, NULL, &actlen ,NULL);
+ ufoma_setup_msg_req(sc, &req);
+ mtx_lock(&sc->sc_mtx);
+ for(i = 0;i < actlen; i++){
+
+ if(ttydisc_rint(sc->sc_ucom.sc_tty, sc->sc_resbuffer[i], 0)
+ == -1){
+ break;
+ }
+ }
+
+ ttydisc_rint_done(sc->sc_ucom.sc_tty);
+
+ sc->sc_nummsg--;
+ if(sc->sc_nummsg){
+ usbd_setup_default_xfer(sc->sc_msgxf, ucom->sc_udev,
+ priv, USBD_DEFAULT_TIMEOUT, &req,
+ sc->sc_resbuffer,
+ UFOMA_CMD_BUF_SIZE,
+ 0, ufoma_msg);
+ usbd_transfer(sc->sc_msgxf);
+ }
+ mtx_unlock(&sc->sc_mtx);
+
+}
+#endif
+static void ufoma_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ufoma_softc *sc = priv;
+ unsigned int a;
+ struct ucom_softc *ucom =&sc->sc_ucom;
+ u_char mstatus;
+
+#ifdef UFOMA_HANDSFREE
+ usb_device_request_t req;
+
+ ufoma_setup_msg_req(sc, &req);
+#endif
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ printf("%s: abnormal status: %s\n", device_get_nameunit(ucom->sc_dev),
+ usbd_errstr(status));
+ return;
+ }
+ if((sc->sc_notify_buf.bmRequestType == UT_READ_VENDOR_INTERFACE)&&
+ (sc->sc_notify_buf.bNotification == UMCPC_REQUEST_ACKNOLEDGE)){
+ a = UGETW(sc->sc_notify_buf.wValue);
+ sc->sc_currentmode = a>>8;
+ if(!(a&0xff)){
+ printf("%s:Mode change Failed\n", device_get_nameunit(ucom->sc_dev));
+ }
+ wakeup(&sc->sc_currentmode);
+ }
+ if(sc->sc_notify_buf.bmRequestType != UCDC_NOTIFICATION){
+ return;
+ }
+ switch(sc->sc_notify_buf.bNotification){
+#ifdef UFOMA_HANDSFREE
+ case UCDC_N_RESPONSE_AVAILABLE:
+ if(sc->sc_is_ucom){
+ printf("%s:wrong response request?\n", device_get_nameunit(ucom->sc_dev));
+ break;
+ }
+ mtx_lock(&sc->sc_mtx);
+ if(!sc->sc_nummsg){
+ usbd_setup_default_xfer(sc->sc_msgxf, ucom->sc_udev,
+ priv, USBD_DEFAULT_TIMEOUT, &req, sc->sc_resbuffer,
+ UFOMA_CMD_BUF_SIZE,
+ 0, ufoma_msg);
+ usbd_transfer(sc->sc_msgxf);
+ }
+ sc->sc_nummsg++;
+ mtx_unlock(&sc->sc_mtx);
+ break;
+#endif
+ case UCDC_N_SERIAL_STATE:
+ if(!sc->sc_is_ucom){
+ printf("%s:wrong sereal request?\n",device_get_nameunit(ucom->sc_dev));
+ break;
+ }
+
+ /*
+ * Set the serial state in ucom driver based on
+ * the bits from the notify message
+ */
+ if (UGETW(sc->sc_notify_buf.wLength) != 2) {
+ printf("%s: Invalid notification length! (%d)\n",
+ device_get_nameunit(ucom->sc_dev),
+ UGETW(sc->sc_notify_buf.wLength));
+ break;
+ }
+ DPRINTF(("%s: notify bytes = %02x%02x\n",
+ device_get_nameunit(ucom->sc_dev),
+ sc->sc_notify_buf.data[0],
+ sc->sc_notify_buf.data[1]));
+ /* Currently, lsr is always zero. */
+ sc->sc_lsr = sc->sc_msr = 0;
+ mstatus = sc->sc_notify_buf.data[0];
+
+ if (ISSET(mstatus, UCDC_N_SERIAL_RI))
+ sc->sc_msr |= SER_RI;
+ if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
+ sc->sc_msr |= SER_DSR;
+ if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
+ sc->sc_msr |= SER_DCD;
+ /* Deferred notifying to the ucom layer */
+ taskqueue_enqueue(taskqueue_swi_giant, &sc->sc_task);
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef UFOMA_HANDSFREE
+struct ttydevsw ufomatty_class ={
+ .tsw_flags = TF_INITLOCK|TF_CALLOUT,
+ .tsw_open = ufoma_open,
+ .tsw_close = ufoma_close,
+ .tsw_outwakeup = ufoma_outwakeup,
+ .tsw_free = ufoma_free
+};
+static void ufoma_free(void *sc)
+{
+
+}
+static int ufoma_init_pseudo_ucom(struct ufoma_softc *sc)
+{
+ struct tty *tp;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ tp = ucom->sc_tty = tty_alloc(&ufomatty_class, sc, &Giant);
+ tty_makedev(tp, NULL, "U%d", device_get_unit(ucom->sc_dev));
+
+ return 0;
+}
+
+
+static int ufoma_open(struct tty * tty)
+{
+
+ struct ufoma_softc *sc = tty_softc(tty);
+
+ if(sc->sc_ucom.sc_dying)
+ return (ENXIO);
+
+ mtx_lock(&sc->sc_mtx);
+ if(sc->sc_isopen){
+ mtx_unlock(&sc->sc_mtx);
+ return EBUSY;
+ }
+ mtx_unlock(&sc->sc_mtx);
+
+ return ufoma_ucom_open(sc, 0);
+}
+
+static void ufoma_close(struct tty *tty)
+{
+ struct ufoma_softc *sc = tty_softc(tty);
+
+ ufoma_ucom_close(sc, 0);
+}
+
+static void ufoma_outwakeup(struct tty *tp)
+{
+ struct ufoma_softc *sc = tty_softc(tp);
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usb_device_request_t req;
+ int len,i;
+ unsigned char buf[128];
+
+ uByte c;
+ if(ucom->sc_dying)
+ return;
+ if(ucom->sc_state &UCS_TXBUSY)
+ return;
+
+ ucom->sc_state |= UCS_TXBUSY;
+ for(;;){
+ len = ttydisc_getc(tp, buf, sizeof(buf));
+ if(len == 0){
+ break;
+ }
+
+ for(i=0; i < len; i++){
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ c = buf[i];
+ req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wValue, 0);
+ USETW(req.wLength, 1);
+ usbd_do_request(ucom->sc_udev, &req, &c);
+ }
+ }
+ ucom->sc_state &= ~(UCS_TXBUSY);
+
+}
+#endif
+
+
+static int ufoma_ucom_open(void *p, int portno)
+{
+ struct ufoma_softc *sc = p;
+ int res;
+
+ if(sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED){
+ if((res = ufoma_link_state(sc))){
+ return res;
+ }
+ }
+
+ sc->sc_cmdbp = 0;
+ if(sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED){
+ if((res = ufoma_activate_state(sc, sc->sc_modetoactivate))){
+ return res;
+ }
+ }
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_isopen = 1;
+ mtx_unlock(&sc->sc_mtx);
+ /*Now line coding should be set.*/
+ if(sc->sc_is_ucom){
+ ufoma_set_line_state(sc);
+ ufoma_set_line_coding(sc, NULL);
+ }
+ return 0;
+}
+
+static void ufoma_ucom_close(void *p, int portno)
+{
+ struct ufoma_softc *sc = p;
+ ufoma_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED);
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_isopen = 0;
+ mtx_unlock(&sc->sc_mtx);
+ return ;
+}
+
+void
+ufoma_break(struct ufoma_softc *sc, int onoff)
+{
+ usb_device_request_t req;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ DPRINTF(("ufoma_break: onoff=%d\n", onoff));
+
+ if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
+ return;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ (void)usbd_do_request(ucom->sc_udev, &req, 0);
+}
+
+void
+ufoma_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct ufoma_softc *sc = addr;
+
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
+
+static void ufoma_set(void * addr, int portno, int reg, int onoff)
+{
+ struct ufoma_softc *sc = addr;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ ufoma_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ ufoma_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ ufoma_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void
+ufoma_set_line_state(struct ufoma_softc *sc)
+{
+ usb_device_request_t req;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ int ls;
+ int err;
+
+ if(!sc->sc_isopen){
+ return ; /*Set it later*/
+ }
+
+ /*Don't send line state emulation request for OBEX port*/
+ if(sc->sc_currentmode == UMCPC_ACM_MODE_OBEX){
+ return;
+ }
+
+ ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
+ (sc->sc_rts ? UCDC_LINE_RTS : 0);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, ls);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(ucom->sc_udev, &req, 0);
+ if(err){
+ printf("LINE_STATE:%s\n", usbd_errstr(err));
+ }
+
+
+}
+
+void
+ufoma_dtr(struct ufoma_softc *sc, int onoff)
+{
+ DPRINTF(("ufoma_foma: onoff=%d\n", onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+ sc->sc_dtr = onoff;
+
+ ufoma_set_line_state(sc);
+}
+
+void
+ufoma_rts(struct ufoma_softc *sc, int onoff)
+{
+ DPRINTF(("ufoma_foma: onoff=%d\n", onoff));
+
+ if (sc->sc_rts == onoff)
+ return;
+ sc->sc_rts = onoff;
+
+ ufoma_set_line_state(sc);
+}
+
+usbd_status
+ufoma_set_line_coding(struct ufoma_softc *sc, usb_cdc_line_state_t *state)
+{
+
+ usbd_status err;
+ usb_device_request_t req;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ if(!sc->sc_isopen){
+ sc->sc_line_state_init = *state;
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ DPRINTF(("ufoma_set_line_coding: rate=%d fmt=%d parity=%d bits=%d\n",
+ UGETDW(state->dwDTERate), state->bCharFormat,
+ state->bParityType, state->bDataBits));
+ if(state == NULL){
+ state = &sc->sc_line_state_init;
+ }else if (memcmp(state, &sc->sc_line_state,
+ UCDC_LINE_STATE_LENGTH) == 0) {
+ DPRINTF(("ufoma_set_line_coding: already set\n"));
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /*Don't send line state emulation request for OBEX port*/
+ if(sc->sc_currentmode == UMCPC_ACM_MODE_OBEX){
+ sc->sc_line_state = *state;
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+ err = usbd_do_request(ucom->sc_udev, &req, state);
+ if (err) {
+ DPRINTF(("ufoma_set_line_coding: failed, err=%s\n",
+ usbd_errstr(err)));
+ return (err);
+ }
+
+ sc->sc_line_state = *state;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static int
+ufoma_param(void *addr, int portno, struct termios *t)
+{
+
+ struct ufoma_softc *sc = addr;
+ usbd_status err;
+ usb_cdc_line_state_t ls;
+
+ DPRINTF(("ufoma_param: sc=%p\n", sc));
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+ if (ISSET(t->c_cflag, CSTOPB))
+ ls.bCharFormat = UCDC_STOP_BIT_2;
+ else
+ ls.bCharFormat = UCDC_STOP_BIT_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ ls.bParityType = UCDC_PARITY_ODD;
+ else
+ ls.bParityType = UCDC_PARITY_EVEN;
+ } else
+ ls.bParityType = UCDC_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ err = ufoma_set_line_coding(sc, &ls);
+ if (err) {
+ DPRINTF(("ufoma_param: err=%s\n", usbd_errstr(err)));
+ return (EIO);
+ }
+
+ return (0);
+}
+
+static int ufoma_init_modem(struct ufoma_softc *sc,struct usb_attach_arg *uaa)
+{
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usb_config_descriptor_t *cd;
+ usb_cdc_acm_descriptor_t *acm;
+ usb_cdc_cm_descriptor_t *cmd;
+ usb_endpoint_descriptor_t *ed;
+ usb_interface_descriptor_t *id;
+ const char *devname = device_get_nameunit(ucom->sc_dev);
+ int i;
+ cd = usbd_get_config_descriptor(ucom->sc_udev);
+ id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
+
+ sc->sc_is_ucom = 1;
+
+ cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+ if(cmd == NULL)
+ return -1;
+ sc->sc_cm_cap = cmd->bmCapabilities;
+
+ acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+ if(acm == NULL)
+ return -1;
+ sc->sc_acm_cap = acm->bmCapabilities;
+
+ sc->sc_data_iface_no = cmd->bDataInterface;
+ printf("%s: data interface %d, has %sCM over data, has %sbreak\n",
+ devname, sc->sc_data_iface_no,
+ sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+ sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+ for(i = 0; i < uaa->nifaces; i++){
+ if(!uaa->ifaces[i])
+ continue;
+ id = usbd_get_interface_descriptor(uaa->ifaces[i]);
+ if(id != NULL &&
+ id->bInterfaceNumber == sc->sc_data_iface_no){
+ sc->sc_data_iface = uaa->ifaces[i];
+ //uaa->ifaces[i] = NULL;
+ }
+ }
+
+ ucom->sc_iface = sc->sc_data_iface;
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ id = usbd_get_interface_descriptor(sc->sc_data_iface);
+ for(i = 0 ; i < id->bNumEndpoints; i++){
+ ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
+ if(ed == NULL){
+ printf("%s: endpoint descriptor for %d\n",
+ devname,i);
+ return -1;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n", devname);
+ return -1;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n", devname);
+ return -1;
+ }
+
+ sc->sc_dtr = -1;
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UMODEMIBUFSIZE;
+ ucom->sc_obufsize = UMODEMOBUFSIZE;
+ ucom->sc_ibufsizepad = UMODEMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &ufoma_callback;
+ TASK_INIT(&sc->sc_task, 0, ufoma_notify, sc);
+ ucom_attach(&sc->sc_ucom);
+
+ return 0;
+}
+
+static void
+ufoma_notify(void *arg, int count)
+{
+ struct ufoma_softc *sc;
+
+ sc = (struct ufoma_softc *)arg;
+ if (sc->sc_ucom.sc_dying)
+ return;
+ ucom_status_change(&sc->sc_ucom);
+}
+static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS)
+{
+ struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+ struct sbuf sb;
+ int i;
+ char *mode;
+
+ sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND);
+ for(i = 1; i < sc->sc_modetable[0]; i++){
+ mode = ufoma_mode_to_str(sc->sc_modetable[i]);
+ if(mode !=NULL){
+ sbuf_cat(&sb, mode);
+ }else{
+ sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]);
+ }
+ if(i < (sc->sc_modetable[0]-1))
+ sbuf_cat(&sb, ",");
+ }
+ sbuf_trim(&sb);
+ sbuf_finish(&sb);
+ sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+ sbuf_delete(&sb);
+
+ return 0;
+}
+static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS)
+{
+ struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+ char *mode;
+ char subbuf[]="(XXX)";
+ mode = ufoma_mode_to_str(sc->sc_currentmode);
+ if(!mode){
+ mode = subbuf;
+ snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode);
+ }
+ sysctl_handle_string(oidp, mode, strlen(mode), req);
+
+ return 0;
+
+}
+static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS)
+{
+ struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+ char *mode;
+ char subbuf[40];
+ int newmode;
+ int error;
+ int i;
+
+ mode = ufoma_mode_to_str(sc->sc_modetoactivate);
+ if(mode){
+ strncpy(subbuf, mode, sizeof(subbuf));
+ }else{
+ snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate);
+ }
+ error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req);
+ if(error != 0 || req->newptr == NULL){
+ return error;
+ }
+
+ if((newmode = ufoma_str_to_mode(subbuf)) == -1){
+ return EINVAL;
+ }
+
+ for(i = 1 ; i < sc->sc_modetable[0] ; i++){
+ if(sc->sc_modetable[i] == newmode){
+ sc->sc_modetoactivate = newmode;
+ return 0;
+ }
+ }
+
+ return EINVAL;
+}
diff --git a/sys/legacy/dev/usb/uftdi.c b/sys/legacy/dev/usb/uftdi.c
new file mode 100644
index 0000000..c652977
--- /dev/null
+++ b/sys/legacy/dev/usb/uftdi.c
@@ -0,0 +1,793 @@
+/* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * FTDI FT8U100AX serial adapter driver
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+
+#include <sys/selinfo.h>
+
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/ucomvar.h>
+
+#include <dev/usb/uftdireg.h>
+
+#ifdef USB_DEBUG
+static int uftdidebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
+SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW,
+ &uftdidebug, 0, "uftdi debug level");
+#define DPRINTF(x) do { \
+ if (uftdidebug) \
+ printf x; \
+ } while (0)
+
+#define DPRINTFN(n, x) do { \
+ if (uftdidebug > (n)) \
+ printf x; \
+ } while (0)
+
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UFTDI_CONFIG_INDEX 0
+#define UFTDI_IFACE_INDEX 0
+
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * The output buffer size cannot be increased due to the size encoding.
+ */
+#define UFTDIIBUFSIZE 256
+#define UFTDIOBUFSIZE 64
+
+struct uftdi_softc {
+ struct ucom_softc sc_ucom;
+ usbd_interface_handle sc_iface; /* interface */
+ enum uftdi_type sc_type;
+ u_int sc_hdrlen;
+ u_char sc_msr;
+ u_char sc_lsr;
+ u_int last_lcr;
+};
+
+static void uftdi_get_status(void *, int portno, u_char *lsr, u_char *msr);
+static void uftdi_set(void *, int, int, int);
+static int uftdi_param(void *, int, struct termios *);
+static int uftdi_open(void *sc, int portno);
+static void uftdi_read(void *sc, int portno, u_char **ptr,u_int32_t *count);
+static size_t uftdi_write(void *sc, int portno, struct tty *,
+ u_char *to, u_int32_t count);
+static void uftdi_break(void *sc, int portno, int onoff);
+static int uftdi_8u232am_getrate(speed_t speed, int *rate);
+
+struct ucom_callback uftdi_callback = {
+ uftdi_get_status,
+ uftdi_set,
+ uftdi_param,
+ NULL,
+ uftdi_open,
+ NULL,
+ uftdi_read,
+ uftdi_write,
+};
+
+static int
+uftdi_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL) {
+ if (uaa->vendor == USB_VENDOR_FTDI &&
+ (uaa->product == USB_PRODUCT_FTDI_SERIAL_2232C))
+ return (UMATCH_VENDOR_IFACESUBCLASS);
+ return (UMATCH_NONE);
+ }
+
+ DPRINTFN(20,("uftdi: vendor=0x%x, product=0x%x\n",
+ uaa->vendor, uaa->product));
+
+ if (uaa->vendor == USB_VENDOR_FTDI &&
+ (uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX ||
+ uaa->product == USB_PRODUCT_FTDI_SERIAL_8U232AM ||
+ uaa->product == USB_PRODUCT_FTDI_SEMC_DSS20 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_631 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_632 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_633 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_634 ||
+ uaa->product == USB_PRODUCT_FTDI_CFA_635 ||
+ uaa->product == USB_PRODUCT_FTDI_USBSERIAL ||
+ uaa->product == USB_PRODUCT_FTDI_MX2_3 ||
+ uaa->product == USB_PRODUCT_FTDI_MX4_5 ||
+ uaa->product == USB_PRODUCT_FTDI_LK202 ||
+ uaa->product == USB_PRODUCT_FTDI_LK204 ||
+ uaa->product == USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M ||
+ uaa->product == USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S ||
+ uaa->product == USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U ||
+ uaa->product == USB_PRODUCT_FTDI_EISCOU ||
+ uaa->product == USB_PRODUCT_FTDI_UOPTBR ||
+ uaa->product == USB_PRODUCT_FTDI_EMCU2D ||
+ uaa->product == USB_PRODUCT_FTDI_PCMSFU ||
+ uaa->product == USB_PRODUCT_FTDI_EMCU2H ||
+ uaa->product == USB_PRODUCT_FTDI_MAXSTREAM ))
+ return (UMATCH_VENDOR_PRODUCT);
+ if (uaa->vendor == USB_VENDOR_SIIG2 &&
+ (uaa->product == USB_PRODUCT_SIIG2_US2308))
+ return (UMATCH_VENDOR_PRODUCT);
+ if (uaa->vendor == USB_VENDOR_INTREPIDCS &&
+ (uaa->product == USB_PRODUCT_INTREPIDCS_VALUECAN ||
+ uaa->product == USB_PRODUCT_INTREPIDCS_NEOVI))
+ return (UMATCH_VENDOR_PRODUCT);
+ if (uaa->vendor == USB_VENDOR_BBELECTRONICS &&
+ (uaa->product == USB_PRODUCT_BBELECTRONICS_USOTL4))
+ return (UMATCH_VENDOR_PRODUCT);
+ if (uaa->vendor == USB_VENDOR_MELCO &&
+ (uaa->product == USB_PRODUCT_MELCO_PCOPRS1))
+ return (UMATCH_VENDOR_PRODUCT);
+ if (uaa->vendor == USB_VENDOR_DRESDENELEKTRONIK &&
+ (uaa->product == USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD))
+ return (UMATCH_VENDOR_PRODUCT);
+
+ return (UMATCH_NONE);
+}
+
+static int
+uftdi_attach(device_t self)
+{
+ struct uftdi_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+ usbd_status err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ DPRINTFN(10,("\nuftdi_attach: sc=%p\n", sc));
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+
+ if (uaa->iface == NULL) {
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UFTDI_CONFIG_INDEX, 1);
+ if (err) {
+ device_printf(ucom->sc_dev,
+ "failed to set configuration, err=%s\n",
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ err = usbd_device2interface_handle(dev, UFTDI_IFACE_INDEX, &iface);
+ if (err) {
+ device_printf(ucom->sc_dev,
+ "failed to get interface, err=%s\n", usbd_errstr(err));
+ goto bad;
+ }
+ } else {
+ iface = uaa->iface;
+ }
+
+ id = usbd_get_interface_descriptor(iface);
+ ucom->sc_iface = iface;
+ switch( uaa->vendor ){
+ case USB_VENDOR_FTDI:
+ switch( uaa->product ){
+ case USB_PRODUCT_FTDI_SERIAL_8U100AX:
+ sc->sc_type = UFTDI_TYPE_SIO;
+ sc->sc_hdrlen = 1;
+ break;
+ case USB_PRODUCT_FTDI_SEMC_DSS20:
+ case USB_PRODUCT_FTDI_SERIAL_8U232AM:
+ case USB_PRODUCT_FTDI_SERIAL_2232C:
+ case USB_PRODUCT_FTDI_CFA_631:
+ case USB_PRODUCT_FTDI_CFA_632:
+ case USB_PRODUCT_FTDI_CFA_633:
+ case USB_PRODUCT_FTDI_CFA_634:
+ case USB_PRODUCT_FTDI_CFA_635:
+ case USB_PRODUCT_FTDI_USBSERIAL:
+ case USB_PRODUCT_FTDI_MX2_3:
+ case USB_PRODUCT_FTDI_MX4_5:
+ case USB_PRODUCT_FTDI_LK202:
+ case USB_PRODUCT_FTDI_LK204:
+ case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M:
+ case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S:
+ case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U:
+ case USB_PRODUCT_FTDI_EISCOU:
+ case USB_PRODUCT_FTDI_UOPTBR:
+ case USB_PRODUCT_FTDI_EMCU2D:
+ case USB_PRODUCT_FTDI_PCMSFU:
+ case USB_PRODUCT_FTDI_EMCU2H:
+ case USB_PRODUCT_FTDI_MAXSTREAM:
+ sc->sc_type = UFTDI_TYPE_8U232AM;
+ sc->sc_hdrlen = 0;
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+ break;
+
+ case USB_VENDOR_INTREPIDCS:
+ switch( uaa->product ){
+ case USB_PRODUCT_INTREPIDCS_VALUECAN:
+ case USB_PRODUCT_INTREPIDCS_NEOVI:
+ sc->sc_type = UFTDI_TYPE_8U232AM;
+ sc->sc_hdrlen = 0;
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+ break;
+
+ case USB_VENDOR_SIIG2:
+ switch( uaa->product ){
+ case USB_PRODUCT_SIIG2_US2308:
+ sc->sc_type = UFTDI_TYPE_8U232AM;
+ sc->sc_hdrlen = 0;
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+ break;
+
+ case USB_VENDOR_BBELECTRONICS:
+ switch( uaa->product ){
+ case USB_PRODUCT_BBELECTRONICS_USOTL4:
+ sc->sc_type = UFTDI_TYPE_8U232AM;
+ sc->sc_hdrlen = 0;
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+ break;
+
+ case USB_VENDOR_MELCO:
+ switch( uaa->product ){
+ case USB_PRODUCT_MELCO_PCOPRS1:
+ sc->sc_type = UFTDI_TYPE_8U232AM;
+ sc->sc_hdrlen = 0;
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+ break;
+
+ case USB_VENDOR_DRESDENELEKTRONIK:
+ switch( uaa->product ){
+ case USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD:
+ sc->sc_type = UFTDI_TYPE_8U232AM;
+ sc->sc_hdrlen = 0;
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+ break;
+
+ default: /* Can't happen */
+ goto bad;
+ }
+
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ int addr, dir, attr;
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ device_printf(ucom->sc_dev,
+ "could not read endpoint descriptor\n");
+ goto bad;
+ }
+
+ addr = ed->bEndpointAddress;
+ dir = UE_GET_DIR(ed->bEndpointAddress);
+ attr = ed->bmAttributes & UE_XFERTYPE;
+ if (dir == UE_DIR_IN && attr == UE_BULK)
+ ucom->sc_bulkin_no = addr;
+ else if (dir == UE_DIR_OUT && attr == UE_BULK)
+ ucom->sc_bulkout_no = addr;
+ else {
+ device_printf(ucom->sc_dev, "unexpected endpoint\n");
+ goto bad;
+ }
+ }
+ if (ucom->sc_bulkin_no == -1) {
+ device_printf(ucom->sc_dev, "Could not find data bulk in\n");
+ goto bad;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ device_printf(ucom->sc_dev, "Could not find data bulk out\n");
+ goto bad;
+ }
+ ucom->sc_parent = sc;
+ if (uaa->iface == NULL)
+ ucom->sc_portno = FTDI_PIT_SIOA;
+ else
+ ucom->sc_portno = FTDI_PIT_SIOA + id->bInterfaceNumber;
+ /* bulkin, bulkout set above */
+
+ ucom->sc_ibufsize = UFTDIIBUFSIZE;
+ ucom->sc_obufsize = UFTDIOBUFSIZE - sc->sc_hdrlen;
+ ucom->sc_ibufsizepad = UFTDIIBUFSIZE;
+ ucom->sc_opkthdrlen = sc->sc_hdrlen;
+
+
+ ucom->sc_callback = &uftdi_callback;
+#if 0
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, ucom->sc_udev,
+ ucom->sc_dev);
+#endif
+ DPRINTF(("uftdi: in=0x%x out=0x%x\n", ucom->sc_bulkin_no, ucom->sc_bulkout_no));
+ ucom_attach(&sc->sc_ucom);
+ return 0;
+
+bad:
+ DPRINTF(("uftdi_attach: ATTACH ERROR\n"));
+ ucom->sc_dying = 1;
+ return ENXIO;
+}
+#if 0
+int
+uftdi_activate(device_t self, enum devact act)
+{
+ struct uftdi_softc *sc = (struct uftdi_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_subdev != NULL)
+ rv = config_deactivate(sc->sc_subdev);
+ sc->sc_ucom.sc_dying = 1;
+ break;
+ }
+ return (rv);
+}
+#endif
+
+static int
+uftdi_detach(device_t self)
+{
+ struct uftdi_softc *sc = device_get_softc(self);
+
+ int rv = 0;
+
+ DPRINTF(("uftdi_detach: sc=%p\n", sc));
+ sc->sc_ucom.sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return rv;
+}
+
+static int
+uftdi_open(void *vsc, int portno)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usb_device_request_t req;
+ usbd_status err;
+ struct termios t;
+
+ DPRINTF(("uftdi_open: sc=%p\n", sc));
+
+ if (ucom->sc_dying)
+ return (EIO);
+
+ /* Perform a full reset on the device */
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_RESET;
+ USETW(req.wValue, FTDI_SIO_RESET_SIO);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ /* Set 9600 baud, 2 stop bits, no parity, 8 bits */
+ t.c_ospeed = 9600;
+ t.c_cflag = CSTOPB | CS8;
+ (void)uftdi_param(sc, portno, &t);
+
+ /* Turn on RTS/CTS flow control */
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ USETW(req.wValue, 0);
+ USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ return (0);
+}
+
+static void
+uftdi_read(void *vsc, int portno, u_char **ptr, u_int32_t *count)
+{
+ struct uftdi_softc *sc = vsc;
+ u_char msr, lsr;
+ unsigned l;
+
+ DPRINTFN(15,("uftdi_read: sc=%p, port=%d count=%d\n",
+ sc, portno, *count));
+ while (*count > 0) {
+ l = *count;
+ if (l > 64)
+ l = 64;
+
+ msr = FTDI_GET_MSR(*ptr);
+ lsr = FTDI_GET_LSR(*ptr);
+
+ if (sc->sc_msr != msr ||
+ (sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK)) {
+ DPRINTF(("uftdi_read: status change msr=0x%02x(0x%02x) "
+ "lsr=0x%02x(0x%02x)\n", msr, sc->sc_msr,
+ lsr, sc->sc_lsr));
+ sc->sc_msr = msr;
+ sc->sc_lsr = lsr;
+ ucom_status_change(&sc->sc_ucom);
+ }
+
+ if (l > 2)
+ ucomrxchars(&sc->sc_ucom, (*ptr) + 2, l - 2);
+ *ptr += l;
+ *count -= l;
+ }
+}
+
+static size_t
+uftdi_write(void *vsc, int portno, struct tty *tp, u_char *to, u_int32_t count)
+{
+ struct uftdi_softc *sc = vsc;
+ size_t l;
+
+ DPRINTFN(10,("uftdi_write: sc=%p, port=%d tp=%p, count=%u\n",
+ vsc, portno, tp, count));
+
+ /* Leave space for the length tag. */
+ l = ttydisc_getc(tp, to + sc->sc_hdrlen, count - sc->sc_hdrlen);
+ if (l == 0)
+ return (0);
+
+ /* Make length tag. */
+ if (sc->sc_hdrlen > 0)
+ *to = FTDI_OUT_TAG(l, portno);
+
+ return (l + sc->sc_hdrlen);
+}
+
+static void
+uftdi_set(void *vsc, int portno, int reg, int onoff)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = vsc;
+ usb_device_request_t req;
+ int ctl;
+
+ DPRINTF(("uftdi_set: sc=%p, port=%d reg=%d onoff=%d\n", vsc, portno,
+ reg, onoff));
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ ctl = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
+ break;
+ case UCOM_SET_RTS:
+ ctl = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
+ break;
+ case UCOM_SET_BREAK:
+ uftdi_break(sc, portno, onoff);
+ return;
+ default:
+ return;
+ }
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_MODEM_CTRL;
+ USETW(req.wValue, ctl);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ DPRINTFN(2,("uftdi_set: reqtype=0x%02x req=0x%02x value=0x%04x "
+ "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest,
+ UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength)));
+ (void)usbd_do_request(ucom->sc_udev, &req, NULL);
+}
+
+static int
+uftdi_param(void *vsc, int portno, struct termios *t)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usb_device_request_t req;
+ usbd_status err;
+ int rate=0, data, flow;
+
+ DPRINTF(("uftdi_param: sc=%p\n", sc));
+
+ if (ucom->sc_dying)
+ return (EIO);
+
+ switch (sc->sc_type) {
+ case UFTDI_TYPE_SIO:
+ switch (t->c_ospeed) {
+ case 300: rate = ftdi_sio_b300; break;
+ case 600: rate = ftdi_sio_b600; break;
+ case 1200: rate = ftdi_sio_b1200; break;
+ case 2400: rate = ftdi_sio_b2400; break;
+ case 4800: rate = ftdi_sio_b4800; break;
+ case 9600: rate = ftdi_sio_b9600; break;
+ case 19200: rate = ftdi_sio_b19200; break;
+ case 38400: rate = ftdi_sio_b38400; break;
+ case 57600: rate = ftdi_sio_b57600; break;
+ case 115200: rate = ftdi_sio_b115200; break;
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case UFTDI_TYPE_8U232AM:
+ if (uftdi_8u232am_getrate(t->c_ospeed, &rate) == -1)
+ return (EINVAL);
+ break;
+ }
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_BAUD_RATE;
+ USETW(req.wValue, rate);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ DPRINTFN(2,("uftdi_param: reqtype=0x%02x req=0x%02x value=0x%04x "
+ "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest,
+ UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength)));
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CSTOPB))
+ data = FTDI_SIO_SET_DATA_STOP_BITS_2;
+ else
+ data = FTDI_SIO_SET_DATA_STOP_BITS_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ data |= FTDI_SIO_SET_DATA_PARITY_ODD;
+ else
+ data |= FTDI_SIO_SET_DATA_PARITY_EVEN;
+ } else
+ data |= FTDI_SIO_SET_DATA_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ data |= FTDI_SIO_SET_DATA_BITS(5);
+ break;
+ case CS6:
+ data |= FTDI_SIO_SET_DATA_BITS(6);
+ break;
+ case CS7:
+ data |= FTDI_SIO_SET_DATA_BITS(7);
+ break;
+ case CS8:
+ data |= FTDI_SIO_SET_DATA_BITS(8);
+ break;
+ }
+ sc->last_lcr = data;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_DATA;
+ USETW(req.wValue, data);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ DPRINTFN(2,("uftdi_param: reqtype=0x%02x req=0x%02x value=0x%04x "
+ "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest,
+ UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength)));
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CRTSCTS)) {
+ flow = FTDI_SIO_RTS_CTS_HS;
+ USETW(req.wValue, 0);
+ } else if (ISSET(t->c_iflag, IXON|IXOFF)) {
+ flow = FTDI_SIO_XON_XOFF_HS;
+ USETW2(req.wValue, t->c_cc[VSTOP], t->c_cc[VSTART]);
+ } else {
+ flow = FTDI_SIO_DISABLE_FLOW_CTRL;
+ USETW(req.wValue, 0);
+ }
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ USETW2(req.wIndex, flow, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ return (0);
+}
+
+void
+uftdi_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
+{
+ struct uftdi_softc *sc = vsc;
+
+ DPRINTF(("uftdi_status: msr=0x%02x lsr=0x%02x\n",
+ sc->sc_msr, sc->sc_lsr));
+
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+}
+
+void
+uftdi_break(void *vsc, int portno, int onoff)
+{
+ struct uftdi_softc *sc = vsc;
+ struct ucom_softc *ucom = vsc;
+
+ usb_device_request_t req;
+ int data;
+
+ DPRINTF(("uftdi_break: sc=%p, port=%d onoff=%d\n", vsc, portno,
+ onoff));
+
+ if (onoff) {
+ data = sc->last_lcr | FTDI_SIO_SET_BREAK;
+ } else {
+ data = sc->last_lcr;
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_DATA;
+ USETW(req.wValue, data);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ (void)usbd_do_request(ucom->sc_udev, &req, NULL);
+}
+
+static int
+uftdi_8u232am_getrate(speed_t speed, int *rate)
+{
+ /* Table of the nearest even powers-of-2 for values 0..15. */
+ static const unsigned char roundoff[16] = {
+ 0, 2, 2, 4, 4, 4, 8, 8,
+ 8, 8, 8, 8, 16, 16, 16, 16,
+ };
+
+ unsigned int d, freq;
+ int result;
+
+ if (speed <= 0)
+ return (-1);
+
+ /* Special cases for 2M and 3M. */
+ if (speed >= 3000000 * 100 / 103 &&
+ speed <= 3000000 * 100 / 97) {
+ result = 0;
+ goto done;
+ }
+ if (speed >= 2000000 * 100 / 103 &&
+ speed <= 2000000 * 100 / 97) {
+ result = 1;
+ goto done;
+ }
+
+ d = (FTDI_8U232AM_FREQ << 4) / speed;
+ d = (d & ~15) + roundoff[d & 15];
+
+ if (d < FTDI_8U232AM_MIN_DIV)
+ d = FTDI_8U232AM_MIN_DIV;
+ else if (d > FTDI_8U232AM_MAX_DIV)
+ d = FTDI_8U232AM_MAX_DIV;
+
+ /*
+ * Calculate the frequency needed for d to exactly divide down
+ * to our target speed, and check that the actual frequency is
+ * within 3% of this.
+ */
+ freq = speed * d;
+ if (freq < (quad_t)(FTDI_8U232AM_FREQ << 4) * 100 / 103 ||
+ freq > (quad_t)(FTDI_8U232AM_FREQ << 4) * 100 / 97)
+ return (-1);
+
+ /*
+ * Pack the divisor into the resultant value. The lower
+ * 14-bits hold the integral part, while the upper 2 bits
+ * encode the fractional component: either 0, 0.5, 0.25, or
+ * 0.125.
+ */
+ result = d >> 4;
+ if (d & 8)
+ result |= 0x4000;
+ else if (d & 4)
+ result |= 0x8000;
+ else if (d & 2)
+ result |= 0xc000;
+
+done:
+ *rate = result;
+ return (0);
+}
+
+static device_method_t uftdi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uftdi_match),
+ DEVMETHOD(device_attach, uftdi_attach),
+ DEVMETHOD(device_detach, uftdi_detach),
+
+ { 0, 0 }
+};
+
+static driver_t uftdi_driver = {
+ "ucom",
+ uftdi_methods,
+ sizeof (struct uftdi_softc)
+};
+
+DRIVER_MODULE(uftdi, uhub, uftdi_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uftdi, usb, 1, 1, 1);
+MODULE_DEPEND(uftdi, ucom,UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
diff --git a/sys/legacy/dev/usb/uftdireg.h b/sys/legacy/dev/usb/uftdireg.h
new file mode 100644
index 0000000..78c9349
--- /dev/null
+++ b/sys/legacy/dev/usb/uftdireg.h
@@ -0,0 +1,338 @@
+/* $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Definitions for the FTDI USB Single Port Serial Converter -
+ * known as FTDI_SIO (Serial Input/Output application of the chipset)
+ *
+ * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
+ * USB on the other.
+ *
+ * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details
+ * of the protocol required to talk to the device and ongoing assistence
+ * during development.
+ *
+ * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc. is the original
+ * author of this file.
+ */
+/* Modified by Lennart Augustsson */
+
+/* Vendor Request Interface */
+#define FTDI_SIO_RESET 0 /* Reset the port */
+#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
+#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */
+#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */
+#define FTDI_SIO_GET_STATUS 5 /* Retrieve current value of status reg */
+#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
+#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
+
+/* Port Identifier Table */
+#define FTDI_PIT_DEFAULT 0 /* SIOA */
+#define FTDI_PIT_SIOA 1 /* SIOA */
+#define FTDI_PIT_SIOB 2 /* SIOB */
+#define FTDI_PIT_PARALLEL 3 /* Parallel */
+
+enum uftdi_type {
+ UFTDI_TYPE_SIO,
+ UFTDI_TYPE_8U232AM
+};
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_RESET
+ * wValue: Control Value
+ * 0 = Reset SIO
+ * 1 = Purge RX buffer
+ * 2 = Purge TX buffer
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * The Reset SIO command has this effect:
+ *
+ * Sets flow control set to 'none'
+ * Event char = 0x0d
+ * Event trigger = disabled
+ * Purge RX buffer
+ * Purge TX buffer
+ * Clear DTR
+ * Clear RTS
+ * baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ *
+ */
+/* FTDI_SIO_RESET */
+#define FTDI_SIO_RESET_SIO 0
+#define FTDI_SIO_RESET_PURGE_RX 1
+#define FTDI_SIO_RESET_PURGE_TX 2
+
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_BAUDRATE
+ * wValue: BaudRate value - see below
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ */
+/* FTDI_SIO_SET_BAUDRATE */
+enum {
+ ftdi_sio_b300 = 0,
+ ftdi_sio_b600 = 1,
+ ftdi_sio_b1200 = 2,
+ ftdi_sio_b2400 = 3,
+ ftdi_sio_b4800 = 4,
+ ftdi_sio_b9600 = 5,
+ ftdi_sio_b19200 = 6,
+ ftdi_sio_b38400 = 7,
+ ftdi_sio_b57600 = 8,
+ ftdi_sio_b115200 = 9
+};
+
+#define FTDI_8U232AM_FREQ 3000000
+
+/* Bounds for normal divisors as 4-bit fixed precision ints. */
+#define FTDI_8U232AM_MIN_DIV 0x20
+#define FTDI_8U232AM_MAX_DIV 0x3fff8
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_DATA
+ * wValue: Data characteristics (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: No
+ *
+ * Data characteristics
+ *
+ * B0..7 Number of data bits
+ * B8..10 Parity
+ * 0 = None
+ * 1 = Odd
+ * 2 = Even
+ * 3 = Mark
+ * 4 = Space
+ * B11..13 Stop Bits
+ * 0 = 1
+ * 1 = 1.5
+ * 2 = 2
+ * B14..15 Reserved
+ *
+ */
+/* FTDI_SIO_SET_DATA */
+#define FTDI_SIO_SET_DATA_BITS(n) (n)
+#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
+#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
+#define FTDI_SIO_SET_BREAK (0x1 << 14)
+
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_MODEM_CTRL
+ * wValue: ControlValue (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ *
+ * ControlValue
+ * B0 DTR state
+ * 0 = reset
+ * 1 = set
+ * B1 RTS state
+ * 0 = reset
+ * 1 = set
+ * B2..7 Reserved
+ * B8 DTR state enable
+ * 0 = ignore
+ * 1 = use DTR state
+ * B9 RTS state enable
+ * 0 = ignore
+ * 1 = use RTS state
+ * B10..15 Reserved
+ */
+/* FTDI_SIO_MODEM_CTRL */
+#define FTDI_SIO_SET_DTR_MASK 0x1
+#define FTDI_SIO_SET_DTR_HIGH (1 | ( FTDI_SIO_SET_DTR_MASK << 8))
+#define FTDI_SIO_SET_DTR_LOW (0 | ( FTDI_SIO_SET_DTR_MASK << 8))
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH (2 | ( FTDI_SIO_SET_RTS_MASK << 8))
+#define FTDI_SIO_SET_RTS_LOW (0 | ( FTDI_SIO_SET_RTS_MASK << 8))
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_FLOW_CTRL
+ * wValue: Xoff/Xon
+ * wIndex: Protocol/Port - hIndex is protocl / lIndex is port
+ * wLength: 0
+ * Data: None
+ *
+ * hIndex protocol is:
+ * B0 Output handshaking using RTS/CTS
+ * 0 = disabled
+ * 1 = enabled
+ * B1 Output handshaking using DTR/DSR
+ * 0 = disabled
+ * 1 = enabled
+ * B2 Xon/Xoff handshaking
+ * 0 = disabled
+ * 1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the
+ * XOFF character and the lValue field contains the XON character.
+ */
+/* FTDI_SIO_SET_FLOW_CTRL */
+#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
+#define FTDI_SIO_RTS_CTS_HS 0x1
+#define FTDI_SIO_DTR_DSR_HS 0x2
+#define FTDI_SIO_XON_XOFF_HS 0x4
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_EVENT_CHAR
+ * wValue: Event Char
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * wValue:
+ * B0..7 Event Character
+ * B8 Event Character Processing
+ * 0 = disabled
+ * 1 = enabled
+ * B9..15 Reserved
+ *
+ * FTDI_SIO_SET_EVENT_CHAR
+ *
+ * Set the special event character for the specified communications port.
+ * If the device sees this character it will immediately return the
+ * data read so far - rather than wait 40ms or until 62 bytes are read
+ * which is what normally happens.
+ */
+
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_ERROR_CHAR
+ * wValue: Error Char
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * Error Char
+ * B0..7 Error Character
+ * B8 Error Character Processing
+ * 0 = disabled
+ * 1 = enabled
+ * B9..15 Reserved
+ *
+ *
+ * FTDI_SIO_SET_ERROR_CHAR
+ * Set the parity error replacement character for the specified communications
+ * port.
+ */
+
+
+/*
+ * BmRequestType: 1100 0000b
+ * bRequest: FTDI_SIO_GET_MODEM_STATUS
+ * wValue: zero
+ * wIndex: Port
+ * wLength: 1
+ * Data: Status
+ *
+ * One byte of data is returned
+ * B0..3 0
+ * B4 CTS
+ * 0 = inactive
+ * 1 = active
+ * B5 DSR
+ * 0 = inactive
+ * 1 = active
+ * B6 Ring Indicator (RI)
+ * 0 = inactive
+ * 1 = active
+ * B7 Receive Line Signal Detect (RLSD)
+ * 0 = inactive
+ * 1 = active
+ *
+ * FTDI_SIO_GET_MODEM_STATUS
+ * Retrieve the current value of the modem status register.
+ */
+#define FTDI_SIO_CTS_MASK 0x10
+#define FTDI_SIO_DSR_MASK 0x20
+#define FTDI_SIO_RI_MASK 0x40
+#define FTDI_SIO_RLSD_MASK 0x80
+
+
+
+/*
+ *
+ * DATA FORMAT
+ *
+ * IN Endpoint
+ *
+ * The device reserves the first two bytes of data on this endpoint to contain
+ * the current values of the modem and line status registers. In the absence of
+ * data, the device generates a message consisting of these two status bytes
+ * every 40 ms.
+ *
+ * Byte 0: Modem Status
+ * NOTE: 4 upper bits have same layout as the MSR register in a 16550
+ *
+ * Offset Description
+ * B0..3 Port
+ * B4 Clear to Send (CTS)
+ * B5 Data Set Ready (DSR)
+ * B6 Ring Indicator (RI)
+ * B7 Receive Line Signal Detect (RLSD)
+ *
+ * Byte 1: Line Status
+ * NOTE: same layout as the LSR register in a 16550
+ *
+ * Offset Description
+ * B0 Data Ready (DR)
+ * B1 Overrun Error (OE)
+ * B2 Parity Error (PE)
+ * B3 Framing Error (FE)
+ * B4 Break Interrupt (BI)
+ * B5 Transmitter Holding Register (THRE)
+ * B6 Transmitter Empty (TEMT)
+ * B7 Error in RCVR FIFO
+ *
+ *
+ * OUT Endpoint
+ *
+ * This device reserves the first bytes of data on this endpoint contain the
+ * length and port identifier of the message. For the FTDI USB Serial converter
+ * the port identifier is always 1.
+ *
+ * Byte 0: Port & length
+ *
+ * Offset Description
+ * B0..1 Port
+ * B2..7 Length of message - (not including Byte 0)
+ *
+ */
+#define FTDI_PORT_MASK 0x0f
+#define FTDI_MSR_MASK 0xf0
+#define FTDI_GET_MSR(p) (((p)[0]) & FTDI_MSR_MASK)
+#define FTDI_GET_LSR(p) ((p)[1])
+#define FTDI_LSR_MASK (~0x60) /* interesting bits */
+#define FTDI_OUT_TAG(len, port) (((len) << 2) | (port))
diff --git a/sys/legacy/dev/usb/ugen.c b/sys/legacy/dev/usb/ugen.c
new file mode 100644
index 0000000..4f03cfb
--- /dev/null
+++ b/sys/legacy/dev/usb/ugen.c
@@ -0,0 +1,1590 @@
+/* $NetBSD: ugen.c,v 1.79 2006/03/01 12:38:13 yamt Exp $ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: ugen.c,v 1.61 2002/09/23 05:51:20 simonb Exp $
+ * $NetBSD: ugen.c,v 1.64 2003/06/28 14:21:46 darrenr Exp $
+ * $NetBSD: ugen.c,v 1.65 2003/06/29 22:30:56 fvdl Exp $
+ * $NetBSD: ugen.c,v 1.68 2004/06/23 02:30:52 mycroft Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * 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.
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/clist.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ugendebug) printf x
+#define DPRINTFN(n,x) if (ugendebug>(n)) printf x
+int ugendebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB ugen");
+SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW,
+ &ugendebug, 0, "ugen debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UGEN_CHUNK 128 /* chunk size for read */
+#define UGEN_IBSIZE 1020 /* buffer size */
+#define UGEN_BBSIZE 1024
+
+#define UGEN_NISOFRAMES 500 /* 0.5 seconds worth */
+#define UGEN_NISOREQS 6 /* number of outstanding xfer requests */
+#define UGEN_NISORFRMS 4 /* number of frames (miliseconds) per req */
+
+struct ugen_endpoint {
+ struct ugen_softc *sc;
+ struct cdev *dev;
+ usb_endpoint_descriptor_t *edesc;
+ usbd_interface_handle iface;
+ int state;
+#define UGEN_ASLP 0x02 /* waiting for data */
+#define UGEN_SHORT_OK 0x04 /* short xfers are OK */
+ usbd_pipe_handle pipeh;
+ struct clist q;
+ struct selinfo rsel;
+ u_char *ibuf; /* start of buffer (circular for isoc) */
+ u_char *fill; /* location for input (isoc) */
+ u_char *limit; /* end of circular buffer (isoc) */
+ u_char *cur; /* current read location (isoc) */
+ u_int32_t timeout;
+ struct isoreq {
+ struct ugen_endpoint *sce;
+ usbd_xfer_handle xfer;
+ void *dmabuf;
+ u_int16_t sizes[UGEN_NISORFRMS];
+ } isoreqs[UGEN_NISOREQS];
+};
+
+struct ugen_softc {
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+ struct cdev *dev;
+
+ char sc_is_open[USB_MAX_ENDPOINTS];
+ struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2];
+#define OUT 0
+#define IN 1
+
+#define UGEN_DEV_REF(dev, sc) \
+ if ((sc)->sc_dying || dev_refthread(dev) == NULL) \
+ return (ENXIO)
+#define UGEN_DEV_RELE(dev, sc) \
+ dev_relthread(dev)
+#define UGEN_DEV_OPEN(dev, sc) \
+ /* handled by dev layer */
+#define UGEN_DEV_CLOSE(dev, sc) \
+ /* handled by dev layer */
+ u_char sc_dying;
+};
+
+d_open_t ugenopen;
+d_close_t ugenclose;
+d_read_t ugenread;
+d_write_t ugenwrite;
+d_ioctl_t ugenioctl;
+d_poll_t ugenpoll;
+d_purge_t ugenpurge;
+
+static struct cdevsw ugenctl_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ugenopen,
+ .d_close = ugenclose,
+ .d_ioctl = ugenioctl,
+ .d_purge = ugenpurge,
+ .d_name = "ugenctl",
+};
+
+static struct cdevsw ugen_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ugenopen,
+ .d_close = ugenclose,
+ .d_read = ugenread,
+ .d_write = ugenwrite,
+ .d_ioctl = ugenioctl,
+ .d_poll = ugenpoll,
+ .d_purge = ugenpurge,
+ .d_name = "ugen",
+};
+
+static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr,
+ usbd_status status);
+static void ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr,
+ usbd_status status);
+static int ugen_do_read(struct ugen_softc *, int, struct uio *, int);
+static int ugen_do_write(struct ugen_softc *, int, struct uio *, int);
+static int ugen_do_ioctl(struct ugen_softc *, int, u_long,
+ caddr_t, int, struct thread *);
+static void ugen_make_devnodes(struct ugen_softc *sc);
+static void ugen_destroy_devnodes(struct ugen_softc *sc);
+static int ugen_set_config(struct ugen_softc *sc, int configno);
+static usb_config_descriptor_t *ugen_get_cdesc(struct ugen_softc *sc,
+ int index, int *lenp);
+static usbd_status ugen_set_interface(struct ugen_softc *, int, int);
+static int ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx);
+
+#define UGENUNIT(n) ((dev2unit(n) >> 4) & 0xf)
+#define UGENENDPOINT(n) (dev2unit(n) & 0xf)
+#define UGENMINOR(u, e) (((u) << 4) | (e))
+
+static device_probe_t ugen_match;
+static device_attach_t ugen_attach;
+static device_detach_t ugen_detach;
+
+static devclass_t ugen_devclass;
+
+static device_method_t ugen_methods[] = {
+ DEVMETHOD(device_probe, ugen_match),
+ DEVMETHOD(device_attach, ugen_attach),
+ DEVMETHOD(device_detach, ugen_detach),
+ {0,0},
+ {0,0}
+};
+
+static driver_t ugen_driver = {
+ "ugen",
+ ugen_methods,
+ sizeof(struct ugen_softc)
+};
+
+MODULE_DEPEND(ugen, usb, 1, 1, 1);
+
+static int
+ugen_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+#if 0
+ if (uaa->matchlvl)
+ return (uaa->matchlvl);
+#endif
+ if (uaa->usegeneric)
+ return (UMATCH_GENERIC);
+ else
+ return (UMATCH_NONE);
+}
+
+static int
+ugen_attach(device_t self)
+{
+ struct ugen_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle udev;
+ usbd_status err;
+ int conf;
+
+ sc->sc_dev = self;
+ sc->sc_udev = udev = uaa->device;
+
+ memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+
+ /* First set configuration index 0, the default one for ugen. */
+ err = usbd_set_config_index(udev, 0, 0);
+ if (err) {
+ printf("%s: setting configuration index 0 failed\n",
+ device_get_nameunit(sc->sc_dev));
+ sc->sc_dying = 1;
+ return (ENXIO);
+ }
+ conf = usbd_get_config_descriptor(udev)->bConfigurationValue;
+
+ /* Set up all the local state for this configuration. */
+ err = ugen_set_config(sc, conf);
+ if (err) {
+ printf("%s: setting configuration %d failed\n",
+ device_get_nameunit(sc->sc_dev), conf);
+ sc->sc_dying = 1;
+ return (ENXIO);
+ }
+
+ /* the main device, ctrl endpoint */
+ sc->dev = make_dev(&ugenctl_cdevsw,
+ UGENMINOR(device_get_unit(sc->sc_dev), 0), UID_ROOT, GID_OPERATOR, 0644,
+ "%s", device_get_nameunit(sc->sc_dev));
+ ugen_make_devnodes(sc);
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
+ return (0);
+}
+
+static void
+ugen_make_devnodes(struct ugen_softc *sc)
+{
+ int endptno;
+ struct cdev *dev;
+
+ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) {
+ if (sc->sc_endpoints[endptno][IN].sc != NULL ||
+ sc->sc_endpoints[endptno][OUT].sc != NULL ) {
+ /* endpt can be 0x81 and 0x01, representing
+ * endpoint address 0x01 and IN/OUT directions.
+ * We map both endpts to the same device,
+ * IN is reading from it, OUT is writing to it.
+ *
+ * In the if clause above we check whether one
+ * of the structs is populated.
+ */
+ dev = make_dev(&ugen_cdevsw,
+ UGENMINOR(device_get_unit(sc->sc_dev), endptno),
+ UID_ROOT, GID_OPERATOR, 0644,
+ "%s.%d",
+ device_get_nameunit(sc->sc_dev), endptno);
+ dev_depends(sc->dev, dev);
+ if (sc->sc_endpoints[endptno][IN].sc != NULL)
+ sc->sc_endpoints[endptno][IN].dev = dev;
+ if (sc->sc_endpoints[endptno][OUT].sc != NULL)
+ sc->sc_endpoints[endptno][OUT].dev = dev;
+ }
+ }
+}
+
+static void
+ugen_destroy_devnodes(struct ugen_softc *sc)
+{
+ int endptno, prev_sc_dying;
+ struct cdev *dev;
+
+ prev_sc_dying = sc->sc_dying;
+ sc->sc_dying = 1;
+ /* destroy all devices for the other (existing) endpoints as well */
+ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) {
+ if (sc->sc_endpoints[endptno][IN].sc != NULL ||
+ sc->sc_endpoints[endptno][OUT].sc != NULL ) {
+ /* endpt can be 0x81 and 0x01, representing
+ * endpoint address 0x01 and IN/OUT directions.
+ * We map both endpoint addresses to the same device,
+ * IN is reading from it, OUT is writing to it.
+ *
+ * In the if clause above we check whether one
+ * of the structs is populated.
+ */
+ if (sc->sc_endpoints[endptno][IN].sc != NULL)
+ dev = sc->sc_endpoints[endptno][IN].dev;
+ else
+ dev = sc->sc_endpoints[endptno][OUT].dev;
+
+ KASSERT(dev != NULL,
+ ("ugen_destroy_devnodes: NULL dev"));
+ if(dev != NULL)
+ destroy_dev(dev);
+
+ sc->sc_endpoints[endptno][IN].sc = NULL;
+ sc->sc_endpoints[endptno][OUT].sc = NULL;
+ }
+ }
+ sc->sc_dying = prev_sc_dying;
+}
+
+static int
+ugen_set_config(struct ugen_softc *sc, int configno)
+{
+ usbd_device_handle dev = sc->sc_udev;
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+ struct ugen_endpoint *sce, **sce_cache, ***sce_cache_arr;
+ u_int8_t niface, niface_cache, nendpt, *nendpt_cache;
+ int ifaceno, endptno, endpt;
+ usbd_status err;
+ int dir;
+
+ DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
+ device_get_nameunit(sc->sc_dev), configno, sc));
+
+ /* We start at 1, not 0, because we don't care whether the
+ * control endpoint is open or not. It is always present.
+ */
+ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++)
+ if (sc->sc_is_open[endptno]) {
+ DPRINTFN(1,
+ ("ugen_set_config: %s - endpoint %d is open\n",
+ device_get_nameunit(sc->sc_dev), endptno));
+ return (USBD_IN_USE);
+ }
+
+ err = usbd_interface_count(dev, &niface);
+ if (err)
+ return (err);
+ /* store an array of endpoint descriptors to clear if the configuration
+ * change succeeds - these aren't available afterwards */
+ nendpt_cache = malloc(sizeof(u_int8_t) * niface, M_TEMP, M_WAITOK |
+ M_ZERO);
+ sce_cache_arr = malloc(sizeof(struct ugen_endpoint **) * niface, M_TEMP,
+ M_WAITOK | M_ZERO);
+ niface_cache = niface;
+
+ for (ifaceno = 0; ifaceno < niface; ifaceno++) {
+ DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
+ err = usbd_device2interface_handle(dev, ifaceno, &iface);
+ if (err)
+ panic("ugen_set_config: can't obtain interface handle");
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ panic("ugen_set_config: endpoint count failed");
+
+ /* store endpoint descriptors for each interface */
+ nendpt_cache[ifaceno] = nendpt;
+ sce_cache = malloc(sizeof(struct ugen_endpoint *) * nendpt, M_TEMP,
+ M_WAITOK);
+ sce_cache_arr[ifaceno] = sce_cache;
+
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce_cache[endptno] = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ }
+ }
+
+ /* Avoid setting the current value. */
+ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) {
+ /* attempt to perform the configuration change */
+ err = usbd_set_config_no(dev, configno, 1);
+ if (err) {
+ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++)
+ free(sce_cache_arr[ifaceno], M_TEMP);
+ free(sce_cache_arr, M_TEMP);
+ free(nendpt_cache, M_TEMP);
+ return (err);
+ }
+ }
+
+ ugen_destroy_devnodes(sc);
+
+ /* now we can clear the old interface's ugen_endpoints */
+ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++) {
+ sce_cache = sce_cache_arr[ifaceno];
+ for(endptno = 0; endptno < nendpt_cache[ifaceno]; endptno++) {
+ sce = sce_cache[endptno];
+ sce->sc = 0;
+ sce->edesc = 0;
+ sce->iface = 0;
+ }
+ }
+
+ /* and free the cache storing them */
+ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++)
+ free(sce_cache_arr[ifaceno], M_TEMP);
+ free(sce_cache_arr, M_TEMP);
+ free(nendpt_cache, M_TEMP);
+
+ /* no endpoints if the device is in the unconfigured state */
+ if (configno != USB_UNCONFIG_NO)
+ {
+ /* set the new configuration's ugen_endpoints */
+ err = usbd_interface_count(dev, &niface);
+ if (err)
+ panic("ugen_set_config: interface count failed");
+
+ memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+ for (ifaceno = 0; ifaceno < niface; ifaceno++) {
+ DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
+ err = usbd_device2interface_handle(dev, ifaceno, &iface);
+ if (err)
+ panic("ugen_set_config: can't obtain interface handle");
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ panic("ugen_set_config: endpoint count failed");
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x"
+ "(%d,%d), sce=%p\n",
+ endptno, endpt, UE_GET_ADDR(endpt),
+ UE_GET_DIR(endpt), sce));
+ sce->sc = sc;
+ sce->edesc = ed;
+ sce->iface = iface;
+ }
+ }
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+int
+ugenopen(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct ugen_softc *sc;
+ int unit = UGENUNIT(dev);
+ int endpt = UGENENDPOINT(dev);
+ usb_endpoint_descriptor_t *edesc;
+ struct ugen_endpoint *sce;
+ int dir, isize;
+ usbd_status err;
+ usbd_xfer_handle xfer;
+ void *buf;
+ int i, j;
+
+ sc = devclass_get_softc(ugen_devclass, unit);
+ if (sc == NULL)
+ return (ENXIO);
+
+ DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
+ flag, mode, unit, endpt));
+
+ if (sc == NULL || sc->sc_dying)
+ return (ENXIO);
+
+ if (sc->sc_is_open[endpt])
+ return (EBUSY);
+
+ if (endpt == USB_CONTROL_ENDPOINT) {
+ sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
+ UGEN_DEV_OPEN(dev, sc);
+ return (0);
+ }
+
+ /* Make sure there are pipes for all directions. */
+ for (dir = OUT; dir <= IN; dir++) {
+ if (flag & (dir == OUT ? FWRITE : FREAD)) {
+ sce = &sc->sc_endpoints[endpt][dir];
+ if (sce->edesc == 0)
+ return (ENXIO);
+ }
+ }
+
+ /* Actually open the pipes. */
+ /* XXX Should back out properly if it fails. */
+ for (dir = OUT; dir <= IN; dir++) {
+ if (!(flag & (dir == OUT ? FWRITE : FREAD)))
+ continue;
+ sce = &sc->sc_endpoints[endpt][dir];
+ sce->state = 0;
+ sce->timeout = USBD_NO_TIMEOUT;
+ DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n",
+ sc, endpt, dir, sce));
+ edesc = sce->edesc;
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ if (dir == OUT) {
+ err = usbd_open_pipe(sce->iface,
+ edesc->bEndpointAddress, 0, &sce->pipeh);
+ if (err)
+ return (EIO);
+ break;
+ }
+ isize = UGETW(edesc->wMaxPacketSize);
+ if (isize == 0) /* shouldn't happen */
+ return (EINVAL);
+ sce->ibuf = malloc(isize, M_USBDEV, M_WAITOK);
+ DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n",
+ endpt, isize));
+ if ((clist_alloc_cblocks(&sce->q, UGEN_IBSIZE,
+ UGEN_IBSIZE), 0) == -1)
+ return (ENOMEM);
+ err = usbd_open_pipe_intr(sce->iface,
+ edesc->bEndpointAddress,
+ USBD_SHORT_XFER_OK, &sce->pipeh, sce,
+ sce->ibuf, isize, ugenintr,
+ USBD_DEFAULT_INTERVAL);
+ if (err) {
+ free(sce->ibuf, M_USBDEV);
+ clist_free_cblocks(&sce->q);
+ return (EIO);
+ }
+ DPRINTFN(5, ("ugenopen: interrupt open done\n"));
+ break;
+ case UE_BULK:
+ err = usbd_open_pipe(sce->iface,
+ edesc->bEndpointAddress, 0, &sce->pipeh);
+ if (err)
+ return (EIO);
+ break;
+ case UE_ISOCHRONOUS:
+ if (dir == OUT)
+ return (EINVAL);
+ isize = UGETW(edesc->wMaxPacketSize);
+ if (isize == 0) /* shouldn't happen */
+ return (EINVAL);
+ sce->ibuf = malloc(isize * UGEN_NISOFRAMES,
+ M_USBDEV, M_WAITOK);
+ sce->cur = sce->fill = sce->ibuf;
+ sce->limit = sce->ibuf + isize * UGEN_NISOFRAMES;
+ DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n",
+ endpt, isize));
+ err = usbd_open_pipe(sce->iface,
+ edesc->bEndpointAddress, 0, &sce->pipeh);
+ if (err) {
+ free(sce->ibuf, M_USBDEV);
+ return (EIO);
+ }
+ for(i = 0; i < UGEN_NISOREQS; ++i) {
+ sce->isoreqs[i].sce = sce;
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ goto bad;
+ sce->isoreqs[i].xfer = xfer;
+ buf = usbd_alloc_buffer
+ (xfer, isize * UGEN_NISORFRMS);
+ if (buf == 0) {
+ i++;
+ goto bad;
+ }
+ sce->isoreqs[i].dmabuf = buf;
+ for(j = 0; j < UGEN_NISORFRMS; ++j)
+ sce->isoreqs[i].sizes[j] = isize;
+ usbd_setup_isoc_xfer
+ (xfer, sce->pipeh, &sce->isoreqs[i],
+ sce->isoreqs[i].sizes,
+ UGEN_NISORFRMS, USBD_NO_COPY,
+ ugen_isoc_rintr);
+ (void)usbd_transfer(xfer);
+ }
+ DPRINTFN(5, ("ugenopen: isoc open done\n"));
+ break;
+ bad:
+ while (--i >= 0) /* implicit buffer free */
+ usbd_free_xfer(sce->isoreqs[i].xfer);
+ return (ENOMEM);
+ case UE_CONTROL:
+ sce->timeout = USBD_DEFAULT_TIMEOUT;
+ return (EINVAL);
+ }
+ }
+ sc->sc_is_open[endpt] = 1;
+ UGEN_DEV_OPEN(dev, sc);
+ return (0);
+}
+
+int
+ugenclose(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ struct ugen_endpoint *sce;
+ int dir;
+ int i;
+
+ sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+
+ DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
+ flag, mode, UGENUNIT(dev), endpt));
+
+#ifdef DIAGNOSTIC
+ if (!sc->sc_is_open[endpt]) {
+ printf("ugenclose: not open\n");
+ return (EINVAL);
+ }
+#endif
+
+ if (endpt == USB_CONTROL_ENDPOINT) {
+ DPRINTFN(5, ("ugenclose: close control\n"));
+ sc->sc_is_open[endpt] = 0;
+ UGEN_DEV_CLOSE(dev, sc);
+ return (0);
+ }
+
+ for (dir = OUT; dir <= IN; dir++) {
+ if (!(flag & (dir == OUT ? FWRITE : FREAD)))
+ continue;
+ sce = &sc->sc_endpoints[endpt][dir];
+ if (sce->pipeh == NULL)
+ continue;
+ DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n",
+ endpt, dir, sce));
+
+ usbd_abort_pipe(sce->pipeh);
+ usbd_close_pipe(sce->pipeh);
+ sce->pipeh = NULL;
+
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ ndflush(&sce->q, sce->q.c_cc);
+ clist_free_cblocks(&sce->q);
+ break;
+ case UE_ISOCHRONOUS:
+ for (i = 0; i < UGEN_NISOREQS; ++i)
+ usbd_free_xfer(sce->isoreqs[i].xfer);
+
+ default:
+ break;
+ }
+
+ if (sce->ibuf != NULL) {
+ free(sce->ibuf, M_USBDEV);
+ sce->ibuf = NULL;
+ clist_free_cblocks(&sce->q);
+ }
+ }
+ sc->sc_is_open[endpt] = 0;
+ UGEN_DEV_CLOSE(dev, sc);
+
+ return (0);
+}
+
+static int
+ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
+{
+ struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN];
+ u_int32_t n, tn;
+ char buf[UGEN_BBSIZE];
+ usbd_xfer_handle xfer;
+ usbd_status err;
+ int s;
+ int error = 0, doneone = 0;
+ u_char buffer[UGEN_CHUNK];
+
+ DPRINTFN(5, ("%s: ugenread: %d\n", device_get_nameunit(sc->sc_dev), endpt));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return (ENODEV);
+
+#ifdef DIAGNOSTIC
+ if (sce->edesc == NULL) {
+ printf("ugenread: no edesc\n");
+ return (EIO);
+ }
+ if (sce->pipeh == NULL) {
+ printf("ugenread: no pipe\n");
+ return (EIO);
+ }
+#endif
+
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ /* Block until activity occurred. */
+ s = splusb();
+ while (sce->q.c_cc == 0) {
+ if (flag & O_NONBLOCK) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ sce->state |= UGEN_ASLP;
+ DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
+ error = tsleep(sce, PZERO | PCATCH, "ugenri",
+ (sce->timeout * hz + 999) / 1000);
+ sce->state &= ~UGEN_ASLP;
+ DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
+ if (sc->sc_dying)
+ error = EIO;
+ if (error == EAGAIN) {
+ error = 0; /* timeout, return 0 bytes */
+ break;
+ }
+ if (error)
+ break;
+ }
+ splx(s);
+
+ /* Transfer as many chunks as possible. */
+ while (sce->q.c_cc > 0 && uio->uio_resid > 0 && !error) {
+ n = min(sce->q.c_cc, uio->uio_resid);
+ if (n > sizeof(buffer))
+ n = sizeof(buffer);
+
+ /* Remove a small chunk from the input queue. */
+ q_to_b(&sce->q, buffer, n);
+ DPRINTFN(5, ("ugenread: got %d chars\n", n));
+
+ /* Copy the data to the user process. */
+ error = uiomove(buffer, n, uio);
+ if (error)
+ break;
+ }
+ break;
+ case UE_BULK:
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ return (ENOMEM);
+ while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0 ||
+ !doneone) {
+ DPRINTFN(1, ("ugenread: start transfer %d bytes\n",n));
+ tn = n;
+ doneone = 1;
+ err = usbd_bulk_transfer(
+ xfer, sce->pipeh,
+ sce->state & UGEN_SHORT_OK ?
+ USBD_SHORT_XFER_OK : 0,
+ sce->timeout, buf, &tn, "ugenrb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ DPRINTFN(1, ("ugenread: got %d bytes\n", tn));
+ error = uiomove(buf, tn, uio);
+ if (error || tn < n)
+ break;
+ }
+ usbd_free_xfer(xfer);
+ break;
+ case UE_ISOCHRONOUS:
+ s = splusb();
+ while (sce->cur == sce->fill) {
+ if (flag & O_NONBLOCK) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ sce->state |= UGEN_ASLP;
+ DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
+ error = tsleep(sce, PZERO | PCATCH, "ugenri",
+ (sce->timeout * hz + 999) / 1000);
+ sce->state &= ~UGEN_ASLP;
+ DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
+ if (sc->sc_dying)
+ error = EIO;
+ if (error == EAGAIN) {
+ error = 0; /* timeout, return 0 bytes */
+ break;
+ }
+ if (error)
+ break;
+ }
+
+ while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) {
+ if (sce->fill > sce->cur)
+ n = min(sce->fill - sce->cur, uio->uio_resid);
+ else
+ n = min(sce->limit - sce->cur, uio->uio_resid);
+
+ DPRINTFN(5, ("ugenread: isoc got %d chars\n", n));
+
+ /* Copy the data to the user process. */
+ error = uiomove(sce->cur, n, uio);
+ if (error)
+ break;
+ sce->cur += n;
+ if(sce->cur >= sce->limit)
+ sce->cur = sce->ibuf;
+ }
+ splx(s);
+ break;
+
+
+ default:
+ return (ENXIO);
+ }
+ return (error);
+}
+
+int
+ugenread(struct cdev *dev, struct uio *uio, int flag)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ UGEN_DEV_REF(dev, sc);
+ error = ugen_do_read(sc, endpt, uio, flag);
+ UGEN_DEV_RELE(dev, sc);
+ return (error);
+}
+
+static int
+ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
+{
+ struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT];
+ u_int32_t n;
+ int error = 0, doneone = 0;
+ char buf[UGEN_BBSIZE];
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ DPRINTFN(5, ("%s: ugenwrite: %d\n", device_get_nameunit(sc->sc_dev), endpt));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return (ENODEV);
+
+#ifdef DIAGNOSTIC
+ if (sce->edesc == NULL) {
+ printf("ugenwrite: no edesc\n");
+ return (EIO);
+ }
+ if (sce->pipeh == NULL) {
+ printf("ugenwrite: no pipe\n");
+ return (EIO);
+ }
+#endif
+
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_BULK:
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ return (EIO);
+ while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0 ||
+ !doneone) {
+ doneone = 1;
+ error = uiomove(buf, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n));
+ err = usbd_bulk_transfer(xfer, sce->pipeh, 0,
+ sce->timeout, buf, &n,"ugenwb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ }
+ usbd_free_xfer(xfer);
+ break;
+ case UE_INTERRUPT:
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ return (EIO);
+ while ((n = min(UGETW(sce->edesc->wMaxPacketSize),
+ uio->uio_resid)) != 0 || !doneone) {
+ doneone = 1;
+ error = uiomove(buf, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n));
+ err = usbd_intr_transfer(xfer, sce->pipeh, 0,
+ sce->timeout, buf, &n, "ugenwi");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ }
+ usbd_free_xfer(xfer);
+ break;
+ default:
+ return (ENXIO);
+ }
+ return (error);
+}
+
+int
+ugenwrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ UGEN_DEV_REF(dev, sc);
+ error = ugen_do_write(sc, endpt, uio, flag);
+ UGEN_DEV_RELE(dev, sc);
+ return (error);
+}
+
+void
+ugenpurge(struct cdev *dev)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_endpoint *sce;
+ struct ugen_softc *sc;
+
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return;
+ sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+ sce = &sc->sc_endpoints[endpt][IN];
+ if (sce->pipeh)
+ usbd_abort_pipe(sce->pipeh);
+ if (sce->state & UGEN_ASLP) {
+ DPRINTFN(5, ("ugenpurge: waking %p\n", sce));
+ wakeup(sce);
+ }
+ selwakeuppri(&sce->rsel, PZERO);
+
+ sce = &sc->sc_endpoints[endpt][OUT];
+ if (sce->pipeh)
+ usbd_abort_pipe(sce->pipeh);
+ if (sce->state & UGEN_ASLP) {
+ DPRINTFN(5, ("ugenpurge: waking %p\n", sce));
+ wakeup(sce);
+ }
+ selwakeuppri(&sce->rsel, PZERO);
+}
+
+static int
+ugen_detach(device_t self)
+{
+ struct ugen_softc *sc = device_get_softc(self);
+ struct ugen_endpoint *sce;
+ int i, dir;
+
+ DPRINTF(("ugen_detach: sc=%p\n", sc));
+
+ sc->sc_dying = 1;
+ /* Abort all pipes. Causes processes waiting for transfer to wake. */
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ for (dir = OUT; dir <= IN; dir++) {
+ sce = &sc->sc_endpoints[i][dir];
+ if (sce->pipeh)
+ usbd_abort_pipe(sce->pipeh);
+ selwakeuppri(&sce->rsel, PZERO);
+ }
+ }
+
+ /* destroy the device for the control endpoint */
+ destroy_dev(sc->dev);
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+ return (0);
+}
+
+static void
+ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ struct ugen_endpoint *sce = addr;
+ /*struct ugen_softc *sc = sce->sc;*/
+ u_int32_t count;
+ u_char *ibuf;
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ugenintr: status=%d\n", status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sce->pipeh);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ ibuf = sce->ibuf;
+
+ DPRINTFN(5, ("ugenintr: xfer=%p status=%d count=%d\n",
+ xfer, status, count));
+ DPRINTFN(5, (" data = %02x %02x %02x\n",
+ ibuf[0], ibuf[1], ibuf[2]));
+
+ (void)b_to_q(ibuf, count, &sce->q);
+
+ if (sce->state & UGEN_ASLP) {
+ sce->state &= ~UGEN_ASLP;
+ DPRINTFN(5, ("ugen_intr: waking %p\n", sce));
+ wakeup(sce);
+ }
+ selwakeuppri(&sce->rsel, PZERO);
+}
+
+static void
+ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr,
+ usbd_status status)
+{
+ struct isoreq *req = addr;
+ struct ugen_endpoint *sce = req->sce;
+ u_int32_t count, n;
+ int i, isize;
+
+ /* Return if we are aborting. */
+ if (status == USBD_CANCELLED)
+ return;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ DPRINTFN(5,("ugen_isoc_rintr: xfer %td, count=%d\n", req - sce->isoreqs,
+ count));
+
+ /* throw away oldest input if the buffer is full */
+ if(sce->fill < sce->cur && sce->cur <= sce->fill + count) {
+ sce->cur += count;
+ if(sce->cur >= sce->limit)
+ sce->cur = sce->ibuf + (sce->limit - sce->cur);
+ DPRINTF(("ugen_isoc_rintr: throwing away %d bytes\n",
+ count));
+ }
+
+ isize = UGETW(sce->edesc->wMaxPacketSize);
+ for (i = 0; i < UGEN_NISORFRMS; i++) {
+ u_int32_t actlen = req->sizes[i];
+ char const *buf = (char const *)req->dmabuf + isize * i;
+
+ /* copy data to buffer */
+ while (actlen > 0) {
+ n = min(actlen, sce->limit - sce->fill);
+ memcpy(sce->fill, buf, n);
+
+ buf += n;
+ actlen -= n;
+ sce->fill += n;
+ if(sce->fill == sce->limit)
+ sce->fill = sce->ibuf;
+ }
+
+ /* setup size for next transfer */
+ req->sizes[i] = isize;
+ }
+
+ usbd_setup_isoc_xfer(xfer, sce->pipeh, req, req->sizes, UGEN_NISORFRMS,
+ USBD_NO_COPY, ugen_isoc_rintr);
+ (void)usbd_transfer(xfer);
+
+ if (sce->state & UGEN_ASLP) {
+ sce->state &= ~UGEN_ASLP;
+ DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce));
+ wakeup(sce);
+ }
+ selwakeuppri(&sce->rsel, PZERO);
+}
+
+static usbd_status
+ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno)
+{
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status err;
+ struct ugen_endpoint *sce, **sce_cache;
+ u_int8_t niface, nendpt, nendpt_cache, endptno, endpt;
+ int dir;
+
+ DPRINTFN(15, ("ugen_set_interface %d %d\n", ifaceidx, altno));
+
+ err = usbd_interface_count(sc->sc_udev, &niface);
+ if (err)
+ return (err);
+ if (ifaceidx < 0 || ifaceidx >= niface)
+ return (USBD_INVAL);
+
+ err = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface);
+ if (err)
+ return (err);
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ return (err);
+
+ /* store an array of endpoint descriptors to clear if the interface
+ * change succeeds - these aren't available afterwards */
+ sce_cache = malloc(sizeof(struct ugen_endpoint *) * nendpt, M_TEMP,
+ M_WAITOK);
+ nendpt_cache = nendpt;
+
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce_cache[endptno] = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ }
+
+ /* change setting */
+ err = usbd_set_interface(iface, altno);
+ if (err) {
+ free(sce_cache, M_TEMP);
+ return (err);
+ }
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ panic("ugen_set_interface: endpoint count failed");
+
+ /* destroy the existing devices, we remake the new ones in a moment */
+ ugen_destroy_devnodes(sc);
+
+ /* now we can clear the old interface's ugen_endpoints */
+ for (endptno = 0; endptno < nendpt_cache; endptno++) {
+ sce = sce_cache[endptno];
+ sce->sc = 0;
+ sce->edesc = 0;
+ sce->iface = 0;
+ }
+ free(sce_cache, M_TEMP);
+
+ /* set the new interface's ugen_endpoints */
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ sce->sc = sc;
+ sce->edesc = ed;
+ sce->iface = iface;
+ }
+
+ /* make the new devices */
+ ugen_make_devnodes(sc);
+
+ return (0);
+}
+
+/* Retrieve a complete descriptor for a certain device and index. */
+static usb_config_descriptor_t *
+ugen_get_cdesc(struct ugen_softc *sc, int index, int *lenp)
+{
+ usb_config_descriptor_t *cdesc, *tdesc, cdescr;
+ int len;
+ usbd_status err;
+
+ if (index == USB_CURRENT_CONFIG_INDEX) {
+ tdesc = usbd_get_config_descriptor(sc->sc_udev);
+ len = UGETW(tdesc->wTotalLength);
+ if (lenp)
+ *lenp = len;
+ cdesc = malloc(len, M_TEMP, M_WAITOK);
+ memcpy(cdesc, tdesc, len);
+ DPRINTFN(5,("ugen_get_cdesc: current, len=%d\n", len));
+ } else {
+ err = usbd_get_config_desc(sc->sc_udev, index, &cdescr);
+ if (err)
+ return (0);
+ len = UGETW(cdescr.wTotalLength);
+ DPRINTFN(5,("ugen_get_cdesc: index=%d, len=%d\n", index, len));
+ if (lenp)
+ *lenp = len;
+ cdesc = malloc(len, M_TEMP, M_WAITOK);
+ err = usbd_get_config_desc_full(sc->sc_udev, index, cdesc, len);
+ if (err) {
+ free(cdesc, M_TEMP);
+ return (0);
+ }
+ }
+ return (cdesc);
+}
+
+static int
+ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx)
+{
+ usbd_interface_handle iface;
+ usbd_status err;
+
+ err = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface);
+ if (err)
+ return (-1);
+ return (usbd_get_interface_altindex(iface));
+}
+
+static int
+ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd,
+ caddr_t addr, int flag, struct thread *p)
+{
+ struct ugen_endpoint *sce;
+ usbd_status err;
+ usbd_interface_handle iface;
+ struct usb_config_desc *cd;
+ usb_config_descriptor_t *cdesc;
+ struct usb_interface_desc *id;
+ usb_interface_descriptor_t *idesc;
+ struct usb_endpoint_desc *ed;
+ usb_endpoint_descriptor_t *edesc;
+ struct usb_alt_interface *ai;
+ struct usb_string_desc *si;
+ u_int8_t conf, alt;
+
+ DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd));
+ if (sc->sc_dying)
+ return (EIO);
+
+ switch (cmd) {
+ case FIONBIO:
+ case FIOASYNC:
+ /* All handled in the upper FS layer. */
+ return (0);
+ case USB_SET_SHORT_XFER:
+ if (endpt == USB_CONTROL_ENDPOINT)
+ return (EINVAL);
+ /* This flag only affects read */
+ sce = &sc->sc_endpoints[endpt][IN];
+
+ if (sce->pipeh == NULL) {
+ printf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n");
+ return (EIO);
+ }
+
+ if (*(int *)addr)
+ sce->state |= UGEN_SHORT_OK;
+ else
+ sce->state &= ~UGEN_SHORT_OK;
+ return (0);
+ case USB_SET_TIMEOUT:
+ sce = &sc->sc_endpoints[endpt][IN];
+ sce->timeout = *(int *)addr;
+ sce = &sc->sc_endpoints[endpt][OUT];
+ sce->timeout = *(int *)addr;
+ return (0);
+ default:
+ break;
+ }
+
+ if (endpt != USB_CONTROL_ENDPOINT)
+ return (EINVAL);
+
+ switch (cmd) {
+#ifdef USB_DEBUG
+ case USB_SETDEBUG:
+ ugendebug = *(int *)addr;
+ break;
+#endif
+ case USB_GET_CONFIG:
+ err = usbd_get_config(sc->sc_udev, &conf);
+ if (err)
+ return (EIO);
+ *(int *)addr = conf;
+ break;
+ case USB_SET_CONFIG:
+ if (!(flag & FWRITE))
+ return (EPERM);
+ err = ugen_set_config(sc, *(int *)addr);
+ switch (err) {
+ case USBD_NORMAL_COMPLETION:
+ ugen_make_devnodes(sc);
+ break;
+ case USBD_IN_USE:
+ return (EBUSY);
+ default:
+ return (EIO);
+ }
+ break;
+ case USB_GET_ALTINTERFACE:
+ ai = (struct usb_alt_interface *)addr;
+ err = usbd_device2interface_handle(sc->sc_udev,
+ ai->uai_interface_index, &iface);
+ if (err)
+ return (EINVAL);
+ idesc = usbd_get_interface_descriptor(iface);
+ if (idesc == NULL)
+ return (EIO);
+ ai->uai_alt_no = idesc->bAlternateSetting;
+ break;
+ case USB_SET_ALTINTERFACE:
+ if (!(flag & FWRITE))
+ return (EPERM);
+ ai = (struct usb_alt_interface *)addr;
+ err = usbd_device2interface_handle(sc->sc_udev,
+ ai->uai_interface_index, &iface);
+ if (err)
+ return (EINVAL);
+ err = ugen_set_interface(sc, ai->uai_interface_index,
+ ai->uai_alt_no);
+ if (err)
+ return (EINVAL);
+ break;
+ case USB_GET_NO_ALT:
+ ai = (struct usb_alt_interface *)addr;
+ cdesc = ugen_get_cdesc(sc, ai->uai_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ idesc = usbd_find_idesc(cdesc, ai->uai_interface_index, 0);
+ if (idesc == NULL) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ ai->uai_alt_no = usbd_get_no_alts(cdesc,
+ idesc->bInterfaceNumber);
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_DEVICE_DESC:
+ *(usb_device_descriptor_t *)addr =
+ *usbd_get_device_descriptor(sc->sc_udev);
+ break;
+ case USB_GET_CONFIG_DESC:
+ cd = (struct usb_config_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, cd->ucd_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ cd->ucd_desc = *cdesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_INTERFACE_DESC:
+ id = (struct usb_interface_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, id->uid_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ if (id->uid_config_index == USB_CURRENT_CONFIG_INDEX &&
+ id->uid_alt_index == USB_CURRENT_ALT_INDEX)
+ alt = ugen_get_alt_index(sc, id->uid_interface_index);
+ else
+ alt = id->uid_alt_index;
+ idesc = usbd_find_idesc(cdesc, id->uid_interface_index, alt);
+ if (idesc == NULL) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ id->uid_desc = *idesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_ENDPOINT_DESC:
+ ed = (struct usb_endpoint_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, ed->ued_config_index, 0);
+ if (cdesc == NULL)
+ return (EINVAL);
+ if (ed->ued_config_index == USB_CURRENT_CONFIG_INDEX &&
+ ed->ued_alt_index == USB_CURRENT_ALT_INDEX)
+ alt = ugen_get_alt_index(sc, ed->ued_interface_index);
+ else
+ alt = ed->ued_alt_index;
+ edesc = usbd_find_edesc(cdesc, ed->ued_interface_index,
+ alt, ed->ued_endpoint_index);
+ if (edesc == NULL) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ ed->ued_desc = *edesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_FULL_DESC:
+ {
+ int len;
+ struct iovec iov;
+ struct uio uio;
+ struct usb_full_desc *fd = (struct usb_full_desc *)addr;
+ int error;
+
+ cdesc = ugen_get_cdesc(sc, fd->ufd_config_index, &len);
+ if (len > fd->ufd_size)
+ len = fd->ufd_size;
+ iov.iov_base = (caddr_t)fd->ufd_data;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_td = p;
+ error = uiomove((void *)cdesc, len, &uio);
+ free(cdesc, M_TEMP);
+ return (error);
+ }
+ case USB_GET_STRING_DESC: {
+ int len;
+ si = (struct usb_string_desc *)addr;
+ err = usbd_get_string_desc(sc->sc_udev, si->usd_string_index,
+ si->usd_language_id, &si->usd_desc, &len);
+ if (err)
+ return (EINVAL);
+ break;
+ }
+ case USB_DO_REQUEST:
+ {
+ struct usb_ctl_request *ur = (void *)addr;
+ int len = UGETW(ur->ucr_request.wLength);
+ struct iovec iov;
+ struct uio uio;
+ void *ptr = 0;
+ int error = 0;
+
+ if (!(flag & FWRITE))
+ return (EPERM);
+ /* Avoid requests that would damage the bus integrity. */
+ if ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
+ ur->ucr_request.bRequest == UR_SET_ADDRESS) ||
+ (ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
+ ur->ucr_request.bRequest == UR_SET_CONFIG) ||
+ (ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE &&
+ ur->ucr_request.bRequest == UR_SET_INTERFACE))
+ return (EINVAL);
+
+ if (len < 0 || len > 32767)
+ return (EINVAL);
+ if (len != 0) {
+ iov.iov_base = (caddr_t)ur->ucr_data;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_rw =
+ ur->ucr_request.bmRequestType & UT_READ ?
+ UIO_READ : UIO_WRITE;
+ uio.uio_td = p;
+ ptr = malloc(len, M_TEMP, M_WAITOK);
+ if (uio.uio_rw == UIO_WRITE) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ sce = &sc->sc_endpoints[endpt][IN];
+ err = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request,
+ ptr, ur->ucr_flags, &ur->ucr_actlen, sce->timeout);
+ if (err) {
+ error = EIO;
+ goto ret;
+ }
+ if (len != 0) {
+ if (uio.uio_rw == UIO_READ) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ ret:
+ if (ptr)
+ free(ptr, M_TEMP);
+ return (error);
+ }
+ case USB_GET_DEVICEINFO:
+ usbd_fill_deviceinfo(sc->sc_udev,
+ (struct usb_device_info *)addr, 1);
+ break;
+ case USB_RESET_DEVICE:
+ err = usbd_reset_device(sc->sc_udev);
+ if (err)
+ return EIO;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+ugenioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
+ struct thread *p)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ UGEN_DEV_REF(dev, sc);
+ error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, p);
+ UGEN_DEV_RELE(dev, sc);
+ return (error);
+}
+
+int
+ugenpoll(struct cdev *dev, int events, struct thread *p)
+{
+ struct ugen_softc *sc;
+ struct ugen_endpoint *sce_in, *sce_out;
+ usb_endpoint_descriptor_t *edesc;
+ int revents = 0;
+ int s;
+
+ sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
+
+ if (sc->sc_dying)
+ return ((events & (POLLIN | POLLOUT | POLLRDNORM |
+ POLLWRNORM)) | POLLHUP);
+ /* Do not allow to poll a control endpoint */
+ if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT)
+ return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
+
+ sce_in = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
+ sce_out = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT];
+ edesc = (sce_in->edesc != NULL) ? sce_in->edesc : sce_out->edesc;
+ KASSERT(edesc != NULL, ("ugenpoll: NULL edesc"));
+ if (sce_in->edesc == NULL || sce_in->pipeh == NULL)
+ sce_in = NULL;
+ if (sce_out->edesc == NULL || sce_out->pipeh == NULL)
+ sce_out = NULL;
+
+ s = splusb();
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ if (sce_in != NULL && (events & (POLLIN | POLLRDNORM))) {
+ if (sce_in->q.c_cc > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(p, &sce_in->rsel);
+ }
+ if (sce_out != NULL && (events & (POLLOUT | POLLWRNORM))) {
+ if (sce_out->q.c_cc > 0)
+ revents |= events & (POLLOUT | POLLWRNORM);
+ else
+ selrecord(p, &sce_out->rsel);
+ }
+ break;
+ case UE_ISOCHRONOUS:
+ if (sce_in != NULL && (events & (POLLIN | POLLRDNORM))) {
+ if (sce_in->cur != sce_in->fill)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(p, &sce_in->rsel);
+ }
+ if (sce_out != NULL && (events & (POLLOUT | POLLWRNORM))) {
+ if (sce_out->cur != sce_out->fill)
+ revents |= events & (POLLOUT | POLLWRNORM);
+ else
+ selrecord(p, &sce_out->rsel);
+ }
+ break;
+ case UE_BULK:
+ /*
+ * We have no easy way of determining if a read will
+ * yield any data or a write will happen.
+ * Pretend they will.
+ */
+ revents |= events &
+ (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM);
+ break;
+ default:
+ break;
+ }
+ splx(s);
+ return (revents);
+}
+
+DRIVER_MODULE(ugen, uhub, ugen_driver, ugen_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/ugraphire_rdesc.h b/sys/legacy/dev/usb/ugraphire_rdesc.h
new file mode 100644
index 0000000..9c5a0c1
--- /dev/null
+++ b/sys/legacy/dev/usb/ugraphire_rdesc.h
@@ -0,0 +1,176 @@
+/* $NetBSD: usb/ugraphire_rdesc.h,v 1.1 2000/12/29 01:47:49 augustss Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2000 Nick Hibma <n_hibma@freebsd.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+static const uByte uhid_graphire_report_descr[] = {
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x01, /* USAGE (Digitizer) */
+ 0xa1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x02, /* REPORT_ID (2) */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x01, /* USAGE (Digitizer) */
+ 0xa1, 0x00, /* COLLECTION (Physical) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+ 0x09, 0x33, /* USAGE (Touch) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x44, /* USAGE (Barrel Switch) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
+ 0x09, 0x3c, /* USAGE (Invert) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x38, /* USAGE (Transducer Index) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x32, /* USAGE (In Range) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x30, /* USAGE (Tip Pressure) */
+ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0xc0, /* END_COLLECTION */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x85, 0x02, /* REPORT_ID (2) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x85, 0x03, /* REPORT_ID (3) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */
+ 0xc0, /* END_COLLECTION */
+};
+
+static const uByte uhid_graphire3_4x5_report_descr[] = {
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x02, /* USAGE (Mouse) */
+ 0xa1, 0x01, /* COLLECTION (Application) */
+ 0x85, 0x01, /* REPORT_ID (1) */
+ 0x09, 0x01, /* USAGE (Pointer) */
+ 0xa1, 0x00, /* COLLECTION (Physical) */
+ 0x05, 0x09, /* USAGE_PAGE (Button) */
+ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
+ 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+ 0x95, 0x03, /* REPORT_COUNT (3) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x75, 0x05, /* REPORT_SIZE (5) */
+ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x09, 0x38, /* USAGE (Wheel) */
+ 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
+ 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */
+ 0x75, 0x08, /* REPORT_SIZE (8) */
+ 0x95, 0x03, /* REPORT_COUNT (3) */
+ 0x81, 0x06, /* INPUT (Data,Var,Rel) */
+ 0xc0, /* END_COLLECTION */
+ 0xc0, /* END_COLLECTION */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x01, /* USAGE (Pointer) */
+ 0xa1, 0x01, /* COLLECTION (Applicaption) */
+ 0x85, 0x02, /* REPORT_ID (2) */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x01, /* USAGE (Digitizer) */
+ 0xa1, 0x00, /* COLLECTION (Physical) */
+ 0x09, 0x33, /* USAGE (Touch) */
+ 0x09, 0x44, /* USAGE (Barrel Switch) */
+ 0x09, 0x44, /* USAGE (Barrel Switch) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x95, 0x03, /* REPORT_COUNT (3) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */
+ 0x09, 0x3c, /* USAGE (Invert) */
+ 0x09, 0x38, /* USAGE (Transducer Index) */
+ 0x09, 0x32, /* USAGE (In Range) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x95, 0x03, /* REPORT_COUNT (3) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x30, /* USAGE (Tip Pressure) */
+ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0xc0, /* END_COLLECTION */
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x85, 0x02, /* REPORT_ID (2) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */
+ 0x09, 0x00, /* USAGE (Undefined) */
+ 0x85, 0x03, /* REPORT_ID (3) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */
+ 0xc0 /* END_COLLECTION */
+};
diff --git a/sys/legacy/dev/usb/uhci.c b/sys/legacy/dev/usb/uhci.c
new file mode 100644
index 0000000..4d4c61b
--- /dev/null
+++ b/sys/legacy/dev/usb/uhci.c
@@ -0,0 +1,3704 @@
+/* $NetBSD: uhci.c,v 1.170 2003/02/19 01:35:04 augustss Exp $ */
+
+/* Also already incorporated from NetBSD:
+ * $NetBSD: uhci.c,v 1.172 2003/02/23 04:19:26 simonb Exp $
+ * $NetBSD: uhci.c,v 1.173 2003/05/13 04:41:59 gson Exp $
+ * $NetBSD: uhci.c,v 1.175 2003/09/12 16:18:08 mycroft Exp $
+ * $NetBSD: uhci.c,v 1.176 2003/11/04 19:11:21 mycroft Exp $
+ * $NetBSD: uhci.c,v 1.177 2003/12/29 08:17:10 toshii Exp $
+ * $NetBSD: uhci.c,v 1.178 2004/03/02 16:32:05 martin Exp $
+ * $NetBSD: uhci.c,v 1.180 2004/07/17 20:12:03 mycroft Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+/*-
+ * 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.
+ */
+
+/*
+ * USB Universal Host Controller driver.
+ * Handles e.g. PIIX3 and PIIX4.
+ *
+ * UHCI spec: http://developer.intel.com/design/USB/UHCI11D.htm
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ * PIIXn spec: ftp://download.intel.com/design/intarch/datashts/29055002.pdf
+ * ftp://download.intel.com/design/intarch/datashts/29056201.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#if defined(DIAGNOSTIC) && defined(__i386__)
+#include <machine/cpu.h>
+#endif
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/uhcireg.h>
+#include <dev/usb/uhcivar.h>
+
+/* Use bandwidth reclamation for control transfers. Some devices choke on it. */
+/*#define UHCI_CTL_LOOP */
+
+#define delay(d) DELAY(d)
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+#ifdef USB_DEBUG
+uhci_softc_t *thesc;
+#define DPRINTF(x) if (uhcidebug) printf x
+#define DPRINTFN(n,x) if (uhcidebug>(n)) printf x
+int uhcidebug = 0;
+int uhcinoloop = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci");
+SYSCTL_INT(_hw_usb_uhci, OID_AUTO, debug, CTLFLAG_RW,
+ &uhcidebug, 0, "uhci debug level");
+SYSCTL_INT(_hw_usb_uhci, OID_AUTO, loop, CTLFLAG_RW,
+ &uhcinoloop, 0, "uhci noloop");
+#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f))
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uhci_pipe {
+ struct usbd_pipe pipe;
+ int nexttoggle;
+
+ u_char aborting;
+ usbd_xfer_handle abortstart, abortend;
+
+ /* Info needed for different pipe kinds. */
+ union {
+ /* Control pipe */
+ struct {
+ uhci_soft_qh_t *sqh;
+ usb_dma_t reqdma;
+ uhci_soft_td_t *setup, *stat;
+ u_int length;
+ } ctl;
+ /* Interrupt pipe */
+ struct {
+ int npoll;
+ int isread;
+ uhci_soft_qh_t **qhs;
+ } intr;
+ /* Bulk pipe */
+ struct {
+ uhci_soft_qh_t *sqh;
+ u_int length;
+ int isread;
+ } bulk;
+ /* Iso pipe */
+ struct iso {
+ uhci_soft_td_t **stds;
+ int next, inuse;
+ } iso;
+ } u;
+};
+
+static void uhci_globalreset(uhci_softc_t *);
+static usbd_status uhci_portreset(uhci_softc_t*, int);
+static void uhci_reset(uhci_softc_t *);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static void uhci_shutdown(void *v);
+static void uhci_power(int, void *);
+#endif
+static usbd_status uhci_run(uhci_softc_t *, int run);
+static uhci_soft_td_t *uhci_alloc_std(uhci_softc_t *);
+static void uhci_free_std(uhci_softc_t *, uhci_soft_td_t *);
+static uhci_soft_qh_t *uhci_alloc_sqh(uhci_softc_t *);
+static void uhci_free_sqh(uhci_softc_t *, uhci_soft_qh_t *);
+static usbd_status uhci_aux_dma_alloc(uhci_softc_t *, uhci_soft_td_t *,
+ void *data, int len);
+static uhci_physaddr_t uhci_aux_dma_prepare(uhci_soft_td_t *, int);
+static void uhci_aux_dma_complete(uhci_soft_td_t *, int);
+#if 0
+static void uhci_enter_ctl_q(uhci_softc_t *, uhci_soft_qh_t *,
+ uhci_intr_info_t *);
+static void uhci_exit_ctl_q(uhci_softc_t *, uhci_soft_qh_t *);
+#endif
+
+static void uhci_free_std_chain(uhci_softc_t *,
+ uhci_soft_td_t *, uhci_soft_td_t *);
+static usbd_status uhci_alloc_std_chain(struct uhci_pipe *,
+ uhci_softc_t *, int, int, u_int16_t,
+ usbd_xfer_handle xfer,
+ uhci_soft_td_t **, uhci_soft_td_t **);
+static void uhci_poll_hub(void *);
+static void uhci_waitintr(uhci_softc_t *, usbd_xfer_handle);
+static void uhci_check_intr(uhci_softc_t *, uhci_intr_info_t *);
+static void uhci_idone(uhci_intr_info_t *);
+
+static void uhci_abort_xfer(usbd_xfer_handle, usbd_status status);
+static void uhci_transfer_complete(usbd_xfer_handle xfer);
+
+static void uhci_timeout(void *);
+static void uhci_timeout_task(void *);
+static void uhci_add_ls_ctrl(uhci_softc_t *, uhci_soft_qh_t *);
+static void uhci_add_hs_ctrl(uhci_softc_t *, uhci_soft_qh_t *);
+static void uhci_add_bulk(uhci_softc_t *, uhci_soft_qh_t *);
+static void uhci_remove_ls_ctrl(uhci_softc_t *,uhci_soft_qh_t *);
+static void uhci_remove_hs_ctrl(uhci_softc_t *,uhci_soft_qh_t *);
+static void uhci_remove_bulk(uhci_softc_t *,uhci_soft_qh_t *);
+static int uhci_str(usb_string_descriptor_t *, int, char *);
+static void uhci_add_loop(uhci_softc_t *sc);
+static void uhci_rem_loop(uhci_softc_t *sc);
+
+static usbd_status uhci_setup_isoc(usbd_pipe_handle pipe);
+static void uhci_device_isoc_enter(usbd_xfer_handle);
+
+static usbd_status uhci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
+static void uhci_freem(struct usbd_bus *, usb_dma_t *);
+
+static usbd_xfer_handle uhci_allocx(struct usbd_bus *);
+static void uhci_freex(struct usbd_bus *, usbd_xfer_handle);
+
+static usbd_status uhci_device_ctrl_transfer(usbd_xfer_handle);
+static usbd_status uhci_device_ctrl_start(usbd_xfer_handle);
+static void uhci_device_ctrl_abort(usbd_xfer_handle);
+static void uhci_device_ctrl_close(usbd_pipe_handle);
+static void uhci_device_ctrl_done(usbd_xfer_handle);
+
+static usbd_status uhci_device_intr_transfer(usbd_xfer_handle);
+static usbd_status uhci_device_intr_start(usbd_xfer_handle);
+static void uhci_device_intr_abort(usbd_xfer_handle);
+static void uhci_device_intr_close(usbd_pipe_handle);
+static void uhci_device_intr_done(usbd_xfer_handle);
+
+static usbd_status uhci_device_bulk_transfer(usbd_xfer_handle);
+static usbd_status uhci_device_bulk_start(usbd_xfer_handle);
+static void uhci_device_bulk_abort(usbd_xfer_handle);
+static void uhci_device_bulk_close(usbd_pipe_handle);
+static void uhci_device_bulk_done(usbd_xfer_handle);
+
+static usbd_status uhci_device_isoc_transfer(usbd_xfer_handle);
+static usbd_status uhci_device_isoc_start(usbd_xfer_handle);
+static void uhci_device_isoc_abort(usbd_xfer_handle);
+static void uhci_device_isoc_close(usbd_pipe_handle);
+static void uhci_device_isoc_done(usbd_xfer_handle);
+
+static usbd_status uhci_root_ctrl_transfer(usbd_xfer_handle);
+static usbd_status uhci_root_ctrl_start(usbd_xfer_handle);
+static void uhci_root_ctrl_abort(usbd_xfer_handle);
+static void uhci_root_ctrl_close(usbd_pipe_handle);
+static void uhci_root_ctrl_done(usbd_xfer_handle);
+
+static usbd_status uhci_root_intr_transfer(usbd_xfer_handle);
+static usbd_status uhci_root_intr_start(usbd_xfer_handle);
+static void uhci_root_intr_abort(usbd_xfer_handle);
+static void uhci_root_intr_close(usbd_pipe_handle);
+static void uhci_root_intr_done(usbd_xfer_handle);
+
+static usbd_status uhci_open(usbd_pipe_handle);
+static void uhci_poll(struct usbd_bus *);
+static void uhci_softintr(void *);
+
+static usbd_status uhci_device_request(usbd_xfer_handle xfer);
+
+static void uhci_add_intr(uhci_softc_t *, uhci_soft_qh_t *);
+static void uhci_remove_intr(uhci_softc_t *, uhci_soft_qh_t *);
+static usbd_status uhci_device_setintr(uhci_softc_t *sc,
+ struct uhci_pipe *pipe, int ival);
+
+static void uhci_device_clear_toggle(usbd_pipe_handle pipe);
+static void uhci_noop(usbd_pipe_handle pipe);
+
+static __inline uhci_soft_qh_t *uhci_find_prev_qh(uhci_soft_qh_t *,
+ uhci_soft_qh_t *);
+
+#ifdef USB_DEBUG
+static void uhci_dump_all(uhci_softc_t *);
+static void uhci_dumpregs(uhci_softc_t *);
+static void uhci_dump_qhs(uhci_soft_qh_t *);
+static void uhci_dump_qh(uhci_soft_qh_t *);
+static void uhci_dump_tds(uhci_soft_td_t *);
+static void uhci_dump_td(uhci_soft_td_t *);
+static void uhci_dump_ii(uhci_intr_info_t *ii);
+void uhci_dump(void);
+#endif
+
+#define UBARR(sc) bus_space_barrier((sc)->iot, (sc)->ioh, 0, (sc)->sc_size, \
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#define UWRITE1(sc, r, x) \
+ do { UBARR(sc); bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UWRITE2(sc, r, x) \
+ do { UBARR(sc); bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UWRITE4(sc, r, x) \
+ do { UBARR(sc); bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->iot, (sc)->ioh, (r)))
+#define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->iot, (sc)->ioh, (r)))
+#define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->iot, (sc)->ioh, (r)))
+
+#define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd)
+#define UHCISTS(sc) UREAD2(sc, UHCI_STS)
+
+#define UHCI_RESET_TIMEOUT 100 /* ms, reset timeout */
+
+#define UHCI_CURFRAME(sc) (UREAD2(sc, UHCI_FRNUM) & UHCI_FRNUM_MASK)
+
+#define UHCI_INTR_ENDPT 1
+
+struct usbd_bus_methods uhci_bus_methods = {
+ uhci_open,
+ uhci_softintr,
+ uhci_poll,
+ uhci_allocm,
+ uhci_freem,
+ uhci_allocx,
+ uhci_freex,
+};
+
+struct usbd_pipe_methods uhci_root_ctrl_methods = {
+ uhci_root_ctrl_transfer,
+ uhci_root_ctrl_start,
+ uhci_root_ctrl_abort,
+ uhci_root_ctrl_close,
+ uhci_noop,
+ uhci_root_ctrl_done,
+};
+
+struct usbd_pipe_methods uhci_root_intr_methods = {
+ uhci_root_intr_transfer,
+ uhci_root_intr_start,
+ uhci_root_intr_abort,
+ uhci_root_intr_close,
+ uhci_noop,
+ uhci_root_intr_done,
+};
+
+struct usbd_pipe_methods uhci_device_ctrl_methods = {
+ uhci_device_ctrl_transfer,
+ uhci_device_ctrl_start,
+ uhci_device_ctrl_abort,
+ uhci_device_ctrl_close,
+ uhci_noop,
+ uhci_device_ctrl_done,
+};
+
+struct usbd_pipe_methods uhci_device_intr_methods = {
+ uhci_device_intr_transfer,
+ uhci_device_intr_start,
+ uhci_device_intr_abort,
+ uhci_device_intr_close,
+ uhci_device_clear_toggle,
+ uhci_device_intr_done,
+};
+
+struct usbd_pipe_methods uhci_device_bulk_methods = {
+ uhci_device_bulk_transfer,
+ uhci_device_bulk_start,
+ uhci_device_bulk_abort,
+ uhci_device_bulk_close,
+ uhci_device_clear_toggle,
+ uhci_device_bulk_done,
+};
+
+struct usbd_pipe_methods uhci_device_isoc_methods = {
+ uhci_device_isoc_transfer,
+ uhci_device_isoc_start,
+ uhci_device_isoc_abort,
+ uhci_device_isoc_close,
+ uhci_noop,
+ uhci_device_isoc_done,
+};
+
+#define uhci_add_intr_info(sc, ii) \
+ LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list)
+#define uhci_del_intr_info(ii) \
+ do { \
+ LIST_REMOVE((ii), list); \
+ (ii)->list.le_prev = NULL; \
+ } while (0)
+#define uhci_active_intr_info(ii) ((ii)->list.le_prev != NULL)
+
+static __inline uhci_soft_qh_t *
+uhci_find_prev_qh(uhci_soft_qh_t *pqh, uhci_soft_qh_t *sqh)
+{
+ DPRINTFN(15,("uhci_find_prev_qh: pqh=%p sqh=%p\n", pqh, sqh));
+
+ for (; pqh->hlink != sqh; pqh = pqh->hlink) {
+#if defined(DIAGNOSTIC) || defined(USB_DEBUG)
+ if (le32toh(pqh->qh.qh_hlink) & UHCI_PTR_T) {
+ printf("uhci_find_prev_qh: QH not found\n");
+ return (NULL);
+ }
+#endif
+ }
+ return (pqh);
+}
+
+void
+uhci_globalreset(uhci_softc_t *sc)
+{
+ UHCICMD(sc, UHCI_CMD_GRESET); /* global reset */
+ usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY); /* wait a little */
+ UHCICMD(sc, 0); /* do nothing */
+}
+
+usbd_status
+uhci_init(uhci_softc_t *sc)
+{
+ usbd_status err;
+ int i, j;
+ uhci_soft_qh_t *clsqh, *chsqh, *bsqh, *sqh, *lsqh;
+ uhci_soft_td_t *std;
+
+ DPRINTFN(1,("uhci_init: start\n"));
+
+#ifdef USB_DEBUG
+ thesc = sc;
+
+ if (uhcidebug > 2)
+ uhci_dumpregs(sc);
+#endif
+
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ uhci_globalreset(sc); /* reset the controller */
+ uhci_reset(sc);
+
+ /* Allocate and initialize real frame array. */
+ err = usb_allocmem(&sc->sc_bus,
+ UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t),
+ UHCI_FRAMELIST_ALIGN, &sc->sc_dma);
+ if (err)
+ return (err);
+ sc->sc_pframes = KERNADDR(&sc->sc_dma, 0);
+ UWRITE2(sc, UHCI_FRNUM, 0); /* set frame number to 0 */
+ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); /* set frame list*/
+
+ /*
+ * Allocate a TD, inactive, that hangs from the last QH.
+ * This is to avoid a bug in the PIIX that makes it run berserk
+ * otherwise.
+ */
+ std = uhci_alloc_std(sc);
+ if (std == NULL)
+ return (USBD_NOMEM);
+ std->link.std = NULL;
+ std->td.td_link = htole32(UHCI_PTR_T);
+ std->td.td_status = htole32(0); /* inactive */
+ std->td.td_token = htole32(0);
+ std->td.td_buffer = htole32(0);
+
+ /* Allocate the dummy QH marking the end and used for looping the QHs.*/
+ lsqh = uhci_alloc_sqh(sc);
+ if (lsqh == NULL)
+ return (USBD_NOMEM);
+ lsqh->hlink = NULL;
+ lsqh->qh.qh_hlink = htole32(UHCI_PTR_T); /* end of QH chain */
+ lsqh->elink = std;
+ lsqh->qh.qh_elink = htole32(std->physaddr | UHCI_PTR_TD);
+ sc->sc_last_qh = lsqh;
+
+ /* Allocate the dummy QH where bulk traffic will be queued. */
+ bsqh = uhci_alloc_sqh(sc);
+ if (bsqh == NULL)
+ return (USBD_NOMEM);
+ bsqh->hlink = lsqh;
+ bsqh->qh.qh_hlink = htole32(lsqh->physaddr | UHCI_PTR_QH);
+ bsqh->elink = NULL;
+ bsqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sc->sc_bulk_start = sc->sc_bulk_end = bsqh;
+
+ /* Allocate dummy QH where high speed control traffic will be queued. */
+ chsqh = uhci_alloc_sqh(sc);
+ if (chsqh == NULL)
+ return (USBD_NOMEM);
+ chsqh->hlink = bsqh;
+ chsqh->qh.qh_hlink = htole32(bsqh->physaddr | UHCI_PTR_QH);
+ chsqh->elink = NULL;
+ chsqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sc->sc_hctl_start = sc->sc_hctl_end = chsqh;
+
+ /* Allocate dummy QH where control traffic will be queued. */
+ clsqh = uhci_alloc_sqh(sc);
+ if (clsqh == NULL)
+ return (USBD_NOMEM);
+ clsqh->hlink = chsqh;
+ clsqh->qh.qh_hlink = htole32(chsqh->physaddr | UHCI_PTR_QH);
+ clsqh->elink = NULL;
+ clsqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sc->sc_lctl_start = sc->sc_lctl_end = clsqh;
+
+ /*
+ * Make all (virtual) frame list pointers point to the interrupt
+ * queue heads and the interrupt queue heads at the control
+ * queue head and point the physical frame list to the virtual.
+ */
+ for(i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = uhci_alloc_std(sc);
+ sqh = uhci_alloc_sqh(sc);
+ if (std == NULL || sqh == NULL)
+ return (USBD_NOMEM);
+ std->link.sqh = sqh;
+ std->td.td_link = htole32(sqh->physaddr | UHCI_PTR_QH);
+ std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */
+ std->td.td_token = htole32(0);
+ std->td.td_buffer = htole32(0);
+ sqh->hlink = clsqh;
+ sqh->qh.qh_hlink = htole32(clsqh->physaddr | UHCI_PTR_QH);
+ sqh->elink = NULL;
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sc->sc_vframes[i].htd = std;
+ sc->sc_vframes[i].etd = std;
+ sc->sc_vframes[i].hqh = sqh;
+ sc->sc_vframes[i].eqh = sqh;
+ for (j = i;
+ j < UHCI_FRAMELIST_COUNT;
+ j += UHCI_VFRAMELIST_COUNT)
+ sc->sc_pframes[j] = htole32(std->physaddr);
+ }
+
+ LIST_INIT(&sc->sc_intrhead);
+
+ STAILQ_INIT(&sc->sc_free_xfers);
+
+ callout_init(&sc->sc_poll_handle, 0);
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &uhci_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct uhci_pipe);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sc->sc_suspend = PWR_RESUME;
+ sc->sc_powerhook = powerhook_establish(uhci_power, sc);
+ sc->sc_shutdownhook = shutdownhook_establish(uhci_shutdown, sc);
+#endif
+
+ DPRINTFN(1,("uhci_init: enabling\n"));
+ UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
+ UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */
+
+ UHCICMD(sc, UHCI_CMD_MAXP); /* Assume 64 byte packets at frame end */
+
+ return (uhci_run(sc, 1)); /* and here we go... */
+}
+
+int
+uhci_detach(struct uhci_softc *sc, int flags)
+{
+ usbd_xfer_handle xfer;
+ int rv = 0;
+
+ sc->sc_dying = 1;
+
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ uhci_run(sc, 0);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ powerhook_disestablish(sc->sc_powerhook);
+ shutdownhook_disestablish(sc->sc_shutdownhook);
+#endif
+
+ /* Free all xfers associated with this HC. */
+ for (;;) {
+ xfer = STAILQ_FIRST(&sc->sc_free_xfers);
+ if (xfer == NULL)
+ break;
+ STAILQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+ free(xfer, M_USB);
+ }
+
+ /* XXX free other data structures XXX */
+ usb_freemem(&sc->sc_bus, &sc->sc_dma);
+
+ return (rv);
+}
+
+usbd_status
+uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
+{
+ return (usb_allocmem(bus, size, 0, dma));
+}
+
+void
+uhci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ usb_freemem(bus, dma);
+}
+
+usbd_xfer_handle
+uhci_allocx(struct usbd_bus *bus)
+{
+ struct uhci_softc *sc = (struct uhci_softc *)bus;
+ usbd_xfer_handle xfer;
+
+ xfer = STAILQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_FREE) {
+ printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer,
+ xfer->busy_free);
+ }
+#endif
+ } else {
+ xfer = malloc(sizeof(struct uhci_xfer), M_USB, M_NOWAIT);
+ }
+ if (xfer != NULL) {
+ memset(xfer, 0, sizeof (struct uhci_xfer));
+ UXFER(xfer)->iinfo.sc = sc;
+ usb_init_task(&UXFER(xfer)->abort_task, uhci_timeout_task,
+ xfer);
+ UXFER(xfer)->uhci_xfer_flags = 0;
+#ifdef DIAGNOSTIC
+ UXFER(xfer)->iinfo.isdone = 1;
+ xfer->busy_free = XFER_BUSY;
+#endif
+ }
+ return (xfer);
+}
+
+void
+uhci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
+{
+ struct uhci_softc *sc = (struct uhci_softc *)bus;
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("uhci_freex: xfer=%p not busy, 0x%08x\n", xfer,
+ xfer->busy_free);
+ return;
+ }
+ xfer->busy_free = XFER_FREE;
+ if (!UXFER(xfer)->iinfo.isdone) {
+ printf("uhci_freex: !isdone\n");
+ return;
+ }
+#endif
+ STAILQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+/*
+ * Shut down the controller when the system is going down.
+ */
+void
+uhci_shutdown(void *v)
+{
+ uhci_softc_t *sc = v;
+
+ DPRINTF(("uhci_shutdown: stopping the HC\n"));
+ uhci_run(sc, 0); /* stop the controller */
+}
+
+/*
+ * Handle suspend/resume.
+ *
+ * We need to switch to polling mode here, because this routine is
+ * called from an interrupt context. This is all right since we
+ * are almost suspended anyway.
+ */
+void
+uhci_power(int why, void *v)
+{
+ uhci_softc_t *sc = v;
+ int cmd;
+ int s;
+
+ s = splhardusb();
+ cmd = UREAD2(sc, UHCI_CMD);
+
+ DPRINTF(("uhci_power: sc=%p, why=%d (was %d), cmd=0x%x\n",
+ sc, why, sc->sc_suspend, cmd));
+
+ if (why != PWR_RESUME) {
+#ifdef USB_DEBUG
+ if (uhcidebug > 2)
+ uhci_dumpregs(sc);
+#endif
+ if (sc->sc_intr_xfer != NULL)
+ callout_stop(&sc->sc_poll_handle);
+ sc->sc_bus.use_polling++;
+ uhci_run(sc, 0); /* stop the controller */
+ cmd &= ~UHCI_CMD_RS;
+
+ /* save some state if BIOS doesn't */
+ sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM);
+ sc->sc_saved_sof = UREAD1(sc, UHCI_SOF);
+
+ UWRITE2(sc, UHCI_INTR, 0); /* disable intrs */
+
+ UHCICMD(sc, cmd | UHCI_CMD_EGSM); /* enter global suspend */
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+ sc->sc_suspend = why;
+ sc->sc_bus.use_polling--;
+ DPRINTF(("uhci_power: cmd=0x%x\n", UREAD2(sc, UHCI_CMD)));
+ } else {
+#ifdef DIAGNOSTIC
+ if (sc->sc_suspend == PWR_RESUME)
+ printf("uhci_power: weird, resume without suspend.\n");
+#endif
+ sc->sc_bus.use_polling++;
+ sc->sc_suspend = why;
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ uhci_globalreset(sc); /* reset the controller */
+ uhci_reset(sc);
+ if (cmd & UHCI_CMD_RS)
+ uhci_run(sc, 0); /* in case BIOS has started it */
+
+ uhci_globalreset(sc);
+ uhci_reset(sc);
+
+ /* restore saved state */
+ UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0));
+ UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum);
+ UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof);
+
+ UHCICMD(sc, cmd | UHCI_CMD_FGR); /* force global resume */
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
+ UHCICMD(sc, cmd & ~UHCI_CMD_EGSM); /* back to normal */
+ UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
+ UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* re-enable intrs */
+ UHCICMD(sc, UHCI_CMD_MAXP);
+ uhci_run(sc, 1); /* and start traffic again */
+ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY);
+ sc->sc_bus.use_polling--;
+ if (sc->sc_intr_xfer != NULL)
+ callout_reset(&sc->sc_poll_handle, sc->sc_ival,
+ uhci_poll_hub, sc->sc_intr_xfer);
+#ifdef USB_DEBUG
+ if (uhcidebug > 2)
+ uhci_dumpregs(sc);
+#endif
+ }
+ splx(s);
+}
+
+#ifdef USB_DEBUG
+static void
+uhci_dumpregs(uhci_softc_t *sc)
+{
+ DPRINTFN(-1,("%s regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, "
+ "flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n",
+ device_get_nameunit(sc->sc_bus.bdev),
+ UREAD2(sc, UHCI_CMD),
+ UREAD2(sc, UHCI_STS),
+ UREAD2(sc, UHCI_INTR),
+ UREAD2(sc, UHCI_FRNUM),
+ UREAD4(sc, UHCI_FLBASEADDR),
+ UREAD1(sc, UHCI_SOF),
+ UREAD2(sc, UHCI_PORTSC1),
+ UREAD2(sc, UHCI_PORTSC2)));
+}
+
+void
+uhci_dump_td(uhci_soft_td_t *p)
+{
+ char sbuf[128], sbuf2[128];
+
+ DPRINTFN(-1,("TD(%p) at %08lx = link=0x%08lx status=0x%08lx "
+ "token=0x%08lx buffer=0x%08lx\n",
+ p, (long)p->physaddr,
+ (long)le32toh(p->td.td_link),
+ (long)le32toh(p->td.td_status),
+ (long)le32toh(p->td.td_token),
+ (long)le32toh(p->td.td_buffer)));
+
+ bitmask_snprintf((u_int32_t)le32toh(p->td.td_link), "\20\1T\2Q\3VF",
+ sbuf, sizeof(sbuf));
+ bitmask_snprintf((u_int32_t)le32toh(p->td.td_status),
+ "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27"
+ "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD",
+ sbuf2, sizeof(sbuf2));
+
+ DPRINTFN(-1,(" %s %s,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d,"
+ "D=%d,maxlen=%d\n", sbuf, sbuf2,
+ UHCI_TD_GET_ERRCNT(le32toh(p->td.td_status)),
+ UHCI_TD_GET_ACTLEN(le32toh(p->td.td_status)),
+ UHCI_TD_GET_PID(le32toh(p->td.td_token)),
+ UHCI_TD_GET_DEVADDR(le32toh(p->td.td_token)),
+ UHCI_TD_GET_ENDPT(le32toh(p->td.td_token)),
+ UHCI_TD_GET_DT(le32toh(p->td.td_token)),
+ UHCI_TD_GET_MAXLEN(le32toh(p->td.td_token))));
+}
+
+void
+uhci_dump_qh(uhci_soft_qh_t *sqh)
+{
+ DPRINTFN(-1,("QH(%p) at %08x: hlink=%08x elink=%08x\n", sqh,
+ (int)sqh->physaddr, le32toh(sqh->qh.qh_hlink),
+ le32toh(sqh->qh.qh_elink)));
+}
+
+
+#if 1
+void
+uhci_dump(void)
+{
+ uhci_dump_all(thesc);
+}
+#endif
+
+void
+uhci_dump_all(uhci_softc_t *sc)
+{
+ uhci_dumpregs(sc);
+ printf("intrs=%d\n", sc->sc_bus.no_intrs);
+ /*printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link);*/
+ uhci_dump_qh(sc->sc_lctl_start);
+}
+
+
+void
+uhci_dump_qhs(uhci_soft_qh_t *sqh)
+{
+ uhci_dump_qh(sqh);
+
+ /* uhci_dump_qhs displays all the QHs and TDs from the given QH onwards
+ * Traverses sideways first, then down.
+ *
+ * QH1
+ * QH2
+ * No QH
+ * TD2.1
+ * TD2.2
+ * TD1.1
+ * etc.
+ *
+ * TD2.x being the TDs queued at QH2 and QH1 being referenced from QH1.
+ */
+
+
+ if (sqh->hlink != NULL && !(le32toh(sqh->qh.qh_hlink) & UHCI_PTR_T))
+ uhci_dump_qhs(sqh->hlink);
+ else
+ DPRINTF(("No QH\n"));
+
+ if (sqh->elink != NULL && !(le32toh(sqh->qh.qh_elink) & UHCI_PTR_T))
+ uhci_dump_tds(sqh->elink);
+ else
+ DPRINTF(("No TD\n"));
+}
+
+void
+uhci_dump_tds(uhci_soft_td_t *std)
+{
+ uhci_soft_td_t *td;
+
+ for(td = std; td != NULL; td = td->link.std) {
+ uhci_dump_td(td);
+
+ /* Check whether the link pointer in this TD marks
+ * the link pointer as end of queue. This avoids
+ * printing the free list in case the queue/TD has
+ * already been moved there (seatbelt).
+ */
+ if (le32toh(td->td.td_link) & UHCI_PTR_T ||
+ le32toh(td->td.td_link) == 0)
+ break;
+ }
+}
+
+static void
+uhci_dump_ii(uhci_intr_info_t *ii)
+{
+ usbd_pipe_handle pipe;
+ usb_endpoint_descriptor_t *ed;
+ usbd_device_handle dev;
+
+#ifdef DIAGNOSTIC
+#define DONE ii->isdone
+#else
+#define DONE 0
+#endif
+ if (ii == NULL) {
+ printf("ii NULL\n");
+ return;
+ }
+ if (ii->xfer == NULL) {
+ printf("ii %p: done=%d xfer=NULL\n",
+ ii, DONE);
+ return;
+ }
+ pipe = ii->xfer->pipe;
+ if (pipe == NULL) {
+ printf("ii %p: done=%d xfer=%p pipe=NULL\n",
+ ii, DONE, ii->xfer);
+ return;
+ }
+ if (pipe->endpoint == NULL) {
+ printf("ii %p: done=%d xfer=%p pipe=%p pipe->endpoint=NULL\n",
+ ii, DONE, ii->xfer, pipe);
+ return;
+ }
+ if (pipe->device == NULL) {
+ printf("ii %p: done=%d xfer=%p pipe=%p pipe->device=NULL\n",
+ ii, DONE, ii->xfer, pipe);
+ return;
+ }
+ ed = pipe->endpoint->edesc;
+ dev = pipe->device;
+ printf("ii %p: done=%d xfer=%p dev=%p vid=0x%04x pid=0x%04x addr=%d pipe=%p ep=0x%02x attr=0x%02x\n",
+ ii, DONE, ii->xfer, dev,
+ UGETW(dev->ddesc.idVendor),
+ UGETW(dev->ddesc.idProduct),
+ dev->address, pipe,
+ ed->bEndpointAddress, ed->bmAttributes);
+#undef DONE
+}
+
+void uhci_dump_iis(struct uhci_softc *sc);
+void
+uhci_dump_iis(struct uhci_softc *sc)
+{
+ uhci_intr_info_t *ii;
+
+ printf("intr_info list:\n");
+ for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list))
+ uhci_dump_ii(ii);
+}
+
+void iidump(void);
+void iidump(void) { uhci_dump_iis(thesc); }
+
+#endif
+
+/*
+ * This routine is executed periodically and simulates interrupts
+ * from the root controller interrupt pipe for port status change.
+ */
+void
+uhci_poll_hub(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ usbd_pipe_handle pipe = xfer->pipe;
+ usbd_device_handle dev = pipe->device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ int s;
+ u_char *p;
+
+ DPRINTFN(20, ("uhci_poll_hub\n"));
+
+ callout_reset(&sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer);
+
+ p = xfer->buffer;
+ p[0] = 0;
+ if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))
+ p[0] |= 1<<1;
+ if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))
+ p[0] |= 1<<2;
+ if (p[0] == 0)
+ /* No change, try again in a while */
+ return;
+
+ xfer->actlen = 1;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ s = splusb();
+ dev->bus->intr_context++;
+ uhci_transfer_complete(xfer);
+ dev->bus->intr_context--;
+ splx(s);
+}
+
+void
+uhci_root_intr_done(usbd_xfer_handle xfer)
+{
+}
+
+void
+uhci_root_ctrl_done(usbd_xfer_handle xfer)
+{
+}
+
+/*
+ * Let the last QH loop back to the high speed control transfer QH.
+ * This is what intel calls "bandwidth reclamation" and improves
+ * USB performance a lot for some devices.
+ * If we are already looping, just count it.
+ */
+void
+uhci_add_loop(uhci_softc_t *sc) {
+#ifdef USB_DEBUG
+ if (uhcinoloop)
+ return;
+#endif
+ if (++sc->sc_loops == 1) {
+ DPRINTFN(5,("uhci_start_loop: add\n"));
+ /* Note, we don't loop back the soft pointer. */
+ sc->sc_last_qh->qh.qh_hlink =
+ htole32(sc->sc_hctl_start->physaddr | UHCI_PTR_QH);
+ }
+}
+
+void
+uhci_rem_loop(uhci_softc_t *sc) {
+#ifdef USB_DEBUG
+ if (uhcinoloop)
+ return;
+#endif
+ if (--sc->sc_loops == 0) {
+ DPRINTFN(5,("uhci_end_loop: remove\n"));
+ sc->sc_last_qh->qh.qh_hlink = htole32(UHCI_PTR_T);
+ }
+}
+
+/* Add high speed control QH, called at splusb(). */
+void
+uhci_add_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *eqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_add_ctrl: sqh=%p\n", sqh));
+ eqh = sc->sc_hctl_end;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ sc->sc_hctl_end = sqh;
+#ifdef UHCI_CTL_LOOP
+ uhci_add_loop(sc);
+#endif
+}
+
+/* Remove high speed control QH, called at splusb(). */
+void
+uhci_remove_hs_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *pqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_remove_hs_ctrl: sqh=%p\n", sqh));
+#ifdef UHCI_CTL_LOOP
+ uhci_rem_loop(sc);
+#endif
+ /*
+ * The T bit should be set in the elink of the QH so that the HC
+ * doesn't follow the pointer. This condition may fail if the
+ * the transferred packet was short so that the QH still points
+ * at the last used TD.
+ * In this case we set the T bit and wait a little for the HC
+ * to stop looking at the TD.
+ */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+
+ pqh = uhci_find_prev_qh(sc->sc_hctl_start, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (sc->sc_hctl_end == sqh)
+ sc->sc_hctl_end = pqh;
+}
+
+/* Add low speed control QH, called at splusb(). */
+void
+uhci_add_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *eqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_add_ls_ctrl: sqh=%p\n", sqh));
+ eqh = sc->sc_lctl_end;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ sc->sc_lctl_end = sqh;
+}
+
+/* Remove low speed control QH, called at splusb(). */
+void
+uhci_remove_ls_ctrl(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *pqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_remove_ls_ctrl: sqh=%p\n", sqh));
+ /* See comment in uhci_remove_hs_ctrl() */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+ pqh = uhci_find_prev_qh(sc->sc_lctl_start, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (sc->sc_lctl_end == sqh)
+ sc->sc_lctl_end = pqh;
+}
+
+/* Add bulk QH, called at splusb(). */
+void
+uhci_add_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *eqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_add_bulk: sqh=%p\n", sqh));
+ eqh = sc->sc_bulk_end;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ sc->sc_bulk_end = sqh;
+ uhci_add_loop(sc);
+}
+
+/* Remove bulk QH, called at splusb(). */
+void
+uhci_remove_bulk(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ uhci_soft_qh_t *pqh;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh));
+ uhci_rem_loop(sc);
+ /* See comment in uhci_remove_hs_ctrl() */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+ pqh = uhci_find_prev_qh(sc->sc_bulk_start, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (sc->sc_bulk_end == sqh)
+ sc->sc_bulk_end = pqh;
+}
+
+static int uhci_intr1(uhci_softc_t *);
+
+int
+uhci_intr(void *arg)
+{
+ uhci_softc_t *sc = arg;
+
+ if (sc->sc_dying)
+ return (0);
+
+ DPRINTFN(15,("uhci_intr: real interrupt\n"));
+ if (sc->sc_bus.use_polling) {
+#ifdef DIAGNOSTIC
+ printf("uhci_intr: ignored interrupt while polling\n");
+#endif
+ return (0);
+ }
+ return (uhci_intr1(sc));
+}
+
+int
+uhci_intr1(uhci_softc_t *sc)
+{
+
+ int status;
+ int ack;
+
+ /*
+ * It can happen that an interrupt will be delivered to
+ * us before the device has been fully attached and the
+ * softc struct has been configured. Usually this happens
+ * when kldloading the USB support as a module after the
+ * system has been booted. If we detect this condition,
+ * we need to squelch the unwanted interrupts until we're
+ * ready for them.
+ */
+ if (sc->sc_bus.bdev == NULL) {
+ UWRITE2(sc, UHCI_STS, 0xFFFF); /* ack pending interrupts */
+ uhci_run(sc, 0); /* stop the controller */
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ return(0);
+ }
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 15) {
+ DPRINTF(("%s: uhci_intr1\n", device_get_nameunit(sc->sc_bus.bdev)));
+ uhci_dumpregs(sc);
+ }
+#endif
+ status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS;
+ if (status == 0) /* The interrupt was not for us. */
+ return (0);
+
+#if defined(DIAGNOSTIC) && defined(__NetBSD__)
+ if (sc->sc_suspend != PWR_RESUME)
+ printf("uhci_intr: suspended sts=0x%x\n", status);
+#endif
+
+ if (sc->sc_suspend != PWR_RESUME) {
+ printf("%s: interrupt while not operating ignored\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ UWRITE2(sc, UHCI_STS, status); /* acknowledge the ints */
+ return (0);
+ }
+
+ ack = 0;
+ if (status & UHCI_STS_USBINT)
+ ack |= UHCI_STS_USBINT;
+ if (status & UHCI_STS_USBEI)
+ ack |= UHCI_STS_USBEI;
+ if (status & UHCI_STS_RD) {
+ ack |= UHCI_STS_RD;
+#ifdef USB_DEBUG
+ printf("%s: resume detect\n", device_get_nameunit(sc->sc_bus.bdev));
+#endif
+ }
+ if (status & UHCI_STS_HSE) {
+ ack |= UHCI_STS_HSE;
+ printf("%s: host system error\n", device_get_nameunit(sc->sc_bus.bdev));
+ }
+ if (status & UHCI_STS_HCPE) {
+ ack |= UHCI_STS_HCPE;
+ printf("%s: host controller process error\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+ }
+ if (status & UHCI_STS_HCH) {
+ /* no acknowledge needed */
+ if (!sc->sc_dying) {
+ printf("%s: host controller halted\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+#ifdef USB_DEBUG
+ uhci_dump_all(sc);
+#endif
+ }
+ sc->sc_dying = 1;
+ }
+
+ if (!ack)
+ return (0); /* nothing to acknowledge */
+ UWRITE2(sc, UHCI_STS, ack); /* acknowledge the ints */
+
+ sc->sc_bus.no_intrs++;
+ usb_schedsoftintr(&sc->sc_bus);
+
+ DPRINTFN(15, ("%s: uhci_intr: exit\n", device_get_nameunit(sc->sc_bus.bdev)));
+
+ return (1);
+}
+
+void
+uhci_softintr(void *v)
+{
+ uhci_softc_t *sc = v;
+ uhci_intr_info_t *ii, *nextii;
+
+ DPRINTFN(10,("%s: uhci_softintr (%d)\n", device_get_nameunit(sc->sc_bus.bdev),
+ sc->sc_bus.intr_context));
+
+ sc->sc_bus.intr_context++;
+
+ /*
+ * Interrupts on UHCI really suck. When the host controller
+ * interrupts because a transfer is completed there is no
+ * way of knowing which transfer it was. You can scan down
+ * the TDs and QHs of the previous frame to limit the search,
+ * but that assumes that the interrupt was not delayed by more
+ * than 1 ms, which may not always be true (e.g. after debug
+ * output on a slow console).
+ * We scan all interrupt descriptors to see if any have
+ * completed.
+ */
+ LIST_FOREACH_SAFE(ii, &sc->sc_intrhead, list, nextii)
+ uhci_check_intr(sc, ii);
+
+#ifdef USB_USE_SOFTINTR
+ if (sc->sc_softwake) {
+ sc->sc_softwake = 0;
+ wakeup(&sc->sc_softwake);
+ }
+#endif /* USB_USE_SOFTINTR */
+
+ sc->sc_bus.intr_context--;
+}
+
+/* Check for an interrupt. */
+void
+uhci_check_intr(uhci_softc_t *sc, uhci_intr_info_t *ii)
+{
+ uhci_soft_td_t *std, *lstd;
+ u_int32_t status;
+
+ DPRINTFN(15, ("uhci_check_intr: ii=%p\n", ii));
+#ifdef DIAGNOSTIC
+ if (ii == NULL) {
+ printf("uhci_check_intr: no ii? %p\n", ii);
+ return;
+ }
+#endif
+ if (ii->xfer->status == USBD_CANCELLED ||
+ ii->xfer->status == USBD_TIMEOUT) {
+ DPRINTF(("uhci_check_intr: aborted xfer=%p\n", ii->xfer));
+ return;
+ }
+
+ if (ii->stdstart == NULL)
+ return;
+ lstd = ii->stdend;
+#ifdef DIAGNOSTIC
+ if (lstd == NULL) {
+ printf("uhci_check_intr: std==0\n");
+ return;
+ }
+#endif
+ /*
+ * If the last TD is still active we need to check whether there
+ * is an error somewhere in the middle, or whether there was a
+ * short packet (SPD and not ACTIVE).
+ */
+ if (le32toh(lstd->td.td_status) & UHCI_TD_ACTIVE) {
+ DPRINTFN(12, ("uhci_check_intr: active ii=%p\n", ii));
+ for (std = ii->stdstart; std != lstd; std = std->link.std) {
+ status = le32toh(std->td.td_status);
+ /* If there's an active TD the xfer isn't done. */
+ if (status & UHCI_TD_ACTIVE)
+ break;
+ /* Any kind of error makes the xfer done. */
+ if (status & UHCI_TD_STALLED)
+ goto done;
+ /* We want short packets, and it is short: it's done */
+ if ((status & UHCI_TD_SPD) &&
+ UHCI_TD_GET_ACTLEN(status) <
+ UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token)))
+ goto done;
+ }
+ DPRINTFN(12, ("uhci_check_intr: ii=%p std=%p still active\n",
+ ii, ii->stdstart));
+ return;
+ }
+ done:
+ DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii));
+ callout_stop(&ii->xfer->timeout_handle);
+ usb_rem_task(ii->xfer->pipe->device, &UXFER(ii->xfer)->abort_task);
+ uhci_idone(ii);
+}
+
+/* Called at splusb() */
+void
+uhci_idone(uhci_intr_info_t *ii)
+{
+ usbd_xfer_handle xfer = ii->xfer;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_td_t *std;
+ u_int32_t status = 0, nstatus;
+ int actlen;
+
+ DPRINTFN(12, ("uhci_idone: ii=%p\n", ii));
+#ifdef DIAGNOSTIC
+ {
+ int s = splhigh();
+ if (ii->isdone) {
+ splx(s);
+#ifdef USB_DEBUG
+ printf("uhci_idone: ii is done!\n ");
+ uhci_dump_ii(ii);
+#else
+ printf("uhci_idone: ii=%p is done!\n", ii);
+#endif
+ return;
+ }
+ ii->isdone = 1;
+ splx(s);
+ }
+#endif
+
+ if (xfer->nframes != 0) {
+ /* Isoc transfer, do things differently. */
+ uhci_soft_td_t **stds = upipe->u.iso.stds;
+ int i, n, nframes, len;
+
+ DPRINTFN(5,("uhci_idone: ii=%p isoc ready\n", ii));
+
+ nframes = xfer->nframes;
+ actlen = 0;
+ n = UXFER(xfer)->curframe;
+ for (i = 0; i < nframes; i++) {
+ std = stds[n];
+#ifdef USB_DEBUG
+ if (uhcidebug > 5) {
+ DPRINTFN(-1,("uhci_idone: isoc TD %d\n", i));
+ uhci_dump_td(std);
+ }
+#endif
+ if (++n >= UHCI_VFRAMELIST_COUNT)
+ n = 0;
+ status = le32toh(std->td.td_status);
+ len = UHCI_TD_GET_ACTLEN(status);
+ xfer->frlengths[i] = len;
+ actlen += len;
+ }
+ upipe->u.iso.inuse -= nframes;
+ xfer->actlen = actlen;
+ xfer->status = USBD_NORMAL_COMPLETION;
+ goto end;
+ }
+
+#ifdef USB_DEBUG
+ DPRINTFN(10, ("uhci_idone: ii=%p, xfer=%p, pipe=%p ready\n",
+ ii, xfer, upipe));
+ if (uhcidebug > 10)
+ uhci_dump_tds(ii->stdstart);
+#endif
+
+ /* The transfer is done, compute actual length and status. */
+ actlen = 0;
+ for (std = ii->stdstart; std != NULL; std = std->link.std) {
+ nstatus = le32toh(std->td.td_status);
+ if (nstatus & UHCI_TD_ACTIVE)
+ break;
+
+ status = nstatus;
+ if (UHCI_TD_GET_PID(le32toh(std->td.td_token)) !=
+ UHCI_TD_PID_SETUP)
+ actlen += UHCI_TD_GET_ACTLEN(status);
+ else {
+ /*
+ * UHCI will report CRCTO in addition to a STALL or NAK
+ * for a SETUP transaction. See section 3.2.2, "TD
+ * CONTROL AND STATUS".
+ */
+ if (status & (UHCI_TD_STALLED | UHCI_TD_NAK))
+ status &= ~UHCI_TD_CRCTO;
+ }
+ }
+ /* If there are left over TDs we need to update the toggle. */
+ if (std != NULL)
+ upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token));
+
+ status &= UHCI_TD_ERROR;
+ DPRINTFN(10, ("uhci_idone: actlen=%d, status=0x%x\n",
+ actlen, status));
+ xfer->actlen = actlen;
+ if (status != 0) {
+#ifdef USB_DEBUG
+ char sbuf[128];
+
+ bitmask_snprintf((u_int32_t)status,
+ "\20\22BITSTUFF\23CRCTO\24NAK\25"
+ "BABBLE\26DBUFFER\27STALLED\30ACTIVE",
+ sbuf, sizeof(sbuf));
+
+ DPRINTFN((status == UHCI_TD_STALLED)*10,
+ ("uhci_idone: error, addr=%d, endpt=0x%02x, "
+ "status 0x%s\n",
+ xfer->pipe->device->address,
+ xfer->pipe->endpoint->edesc->bEndpointAddress,
+ sbuf));
+#endif
+
+ if (status == UHCI_TD_STALLED)
+ xfer->status = USBD_STALLED;
+ else
+ xfer->status = USBD_IOERROR; /* more info XXX */
+ } else {
+ xfer->status = USBD_NORMAL_COMPLETION;
+ }
+
+ end:
+ uhci_transfer_complete(xfer);
+ DPRINTFN(12, ("uhci_idone: ii=%p done\n", ii));
+}
+
+/*
+ * Called when a request does not complete.
+ */
+void
+uhci_timeout(void *addr)
+{
+ uhci_intr_info_t *ii = addr;
+ struct uhci_xfer *uxfer = UXFER(ii->xfer);
+ struct uhci_pipe *upipe = (struct uhci_pipe *)uxfer->xfer.pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
+
+ DPRINTF(("uhci_timeout: uxfer=%p\n", uxfer));
+
+ if (sc->sc_dying) {
+ uhci_abort_xfer(&uxfer->xfer, USBD_TIMEOUT);
+ return;
+ }
+
+ /* Execute the abort in a process context. */
+ usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task,
+ USB_TASKQ_HC);
+}
+
+void
+uhci_timeout_task(void *addr)
+{
+ usbd_xfer_handle xfer = addr;
+ int s;
+
+ DPRINTF(("uhci_timeout_task: xfer=%p\n", xfer));
+
+ s = splusb();
+ uhci_abort_xfer(xfer, USBD_TIMEOUT);
+ splx(s);
+}
+
+/*
+ * Wait here until controller claims to have an interrupt.
+ * Then call uhci_intr and return. Use timeout to avoid waiting
+ * too long.
+ * Only used during boot when interrupts are not enabled yet.
+ */
+void
+uhci_waitintr(uhci_softc_t *sc, usbd_xfer_handle xfer)
+{
+ int timo = xfer->timeout;
+ uhci_intr_info_t *ii;
+
+ DPRINTFN(10,("uhci_waitintr: timeout = %dms\n", timo));
+
+ xfer->status = USBD_IN_PROGRESS;
+ for (; timo >= 0; timo--) {
+ usb_delay_ms(&sc->sc_bus, 1);
+ DPRINTFN(20,("uhci_waitintr: 0x%04x\n", UREAD2(sc, UHCI_STS)));
+ if (UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS)
+ uhci_intr1(sc);
+ if (xfer->status != USBD_IN_PROGRESS)
+ return;
+ }
+
+ /* Timeout */
+ DPRINTF(("uhci_waitintr: timeout\n"));
+ for (ii = LIST_FIRST(&sc->sc_intrhead);
+ ii != NULL && ii->xfer != xfer;
+ ii = LIST_NEXT(ii, list))
+ ;
+#ifdef DIAGNOSTIC
+ if (ii == NULL)
+ panic("uhci_waitintr: lost intr_info");
+#endif
+ uhci_idone(ii);
+}
+
+void
+uhci_poll(struct usbd_bus *bus)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)bus;
+
+ if (UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS)
+ uhci_intr1(sc);
+}
+
+void
+uhci_reset(uhci_softc_t *sc)
+{
+ int n;
+
+ UHCICMD(sc, UHCI_CMD_HCRESET);
+ /* The reset bit goes low when the controller is done. */
+ for (n = 0; n < UHCI_RESET_TIMEOUT &&
+ (UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET); n++)
+ usb_delay_ms(&sc->sc_bus, 1);
+ if (n >= UHCI_RESET_TIMEOUT)
+ printf("%s: controller did not reset\n",
+ device_get_nameunit(sc->sc_bus.bdev));
+}
+
+usbd_status
+uhci_run(uhci_softc_t *sc, int run)
+{
+ int s, n, running;
+ u_int16_t cmd;
+
+ run = run != 0;
+ s = splhardusb();
+ DPRINTF(("uhci_run: setting run=%d\n", run));
+ cmd = UREAD2(sc, UHCI_CMD);
+ if (run)
+ cmd |= UHCI_CMD_RS;
+ else
+ cmd &= ~UHCI_CMD_RS;
+ UHCICMD(sc, cmd);
+ for(n = 0; n < 10; n++) {
+ running = !(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH);
+ /* return when we've entered the state we want */
+ if (run == running) {
+ splx(s);
+ DPRINTF(("uhci_run: done cmd=0x%x sts=0x%x\n",
+ UREAD2(sc, UHCI_CMD), UREAD2(sc, UHCI_STS)));
+ return (USBD_NORMAL_COMPLETION);
+ }
+ usb_delay_ms(&sc->sc_bus, 1);
+ }
+ splx(s);
+ printf("%s: cannot %s\n", device_get_nameunit(sc->sc_bus.bdev),
+ run ? "start" : "stop");
+ return (USBD_IOERROR);
+}
+
+/*
+ * Memory management routines.
+ * uhci_alloc_std allocates TDs
+ * uhci_alloc_sqh allocates QHs
+ * These two routines do their own free list management,
+ * partly for speed, partly because allocating DMAable memory
+ * has page size granularaity so much memory would be wasted if
+ * only one TD/QH (32 bytes) was placed in each allocated chunk.
+ */
+
+uhci_soft_td_t *
+uhci_alloc_std(uhci_softc_t *sc)
+{
+ uhci_soft_td_t *std;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freetds == NULL) {
+ DPRINTFN(2,("uhci_alloc_std: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, UHCI_STD_SIZE * UHCI_STD_CHUNK,
+ UHCI_TD_ALIGN, &dma);
+ if (err)
+ return (0);
+ for(i = 0; i < UHCI_STD_CHUNK; i++) {
+ offs = i * UHCI_STD_SIZE;
+ std = KERNADDR(&dma, offs);
+ std->physaddr = DMAADDR(&dma, offs);
+ std->link.std = sc->sc_freetds;
+ std->aux_dma.block = NULL;
+ std->aux_data = NULL;
+ std->aux_len = 0;
+ sc->sc_freetds = std;
+ }
+ }
+ std = sc->sc_freetds;
+ sc->sc_freetds = std->link.std;
+ memset(&std->td, 0, sizeof(uhci_td_t));
+ return std;
+}
+
+void
+uhci_free_std(uhci_softc_t *sc, uhci_soft_td_t *std)
+{
+#ifdef DIAGNOSTIC
+#define TD_IS_FREE 0x12345678
+ if (le32toh(std->td.td_token) == TD_IS_FREE) {
+ printf("uhci_free_std: freeing free TD %p\n", std);
+ return;
+ }
+ std->td.td_token = htole32(TD_IS_FREE);
+#endif
+ if (std->aux_dma.block != NULL) {
+ usb_freemem(&sc->sc_bus, &std->aux_dma);
+ std->aux_dma.block = NULL;
+ std->aux_data = NULL;
+ std->aux_len = 0;
+ }
+ std->link.std = sc->sc_freetds;
+ sc->sc_freetds = std;
+}
+
+uhci_soft_qh_t *
+uhci_alloc_sqh(uhci_softc_t *sc)
+{
+ uhci_soft_qh_t *sqh;
+ usbd_status err;
+ int i, offs;
+ usb_dma_t dma;
+
+ if (sc->sc_freeqhs == NULL) {
+ DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n"));
+ err = usb_allocmem(&sc->sc_bus, UHCI_SQH_SIZE * UHCI_SQH_CHUNK,
+ UHCI_QH_ALIGN, &dma);
+ if (err)
+ return (0);
+ for(i = 0; i < UHCI_SQH_CHUNK; i++) {
+ offs = i * UHCI_SQH_SIZE;
+ sqh = KERNADDR(&dma, offs);
+ sqh->physaddr = DMAADDR(&dma, offs);
+ sqh->hlink = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+ }
+ }
+ sqh = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh->hlink;
+ memset(&sqh->qh, 0, sizeof(uhci_qh_t));
+ return (sqh);
+}
+
+void
+uhci_free_sqh(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ sqh->hlink = sc->sc_freeqhs;
+ sc->sc_freeqhs = sqh;
+}
+
+void
+uhci_free_std_chain(uhci_softc_t *sc, uhci_soft_td_t *std,
+ uhci_soft_td_t *stdend)
+{
+ uhci_soft_td_t *p;
+
+ for (; std != stdend; std = p) {
+ p = std->link.std;
+ uhci_free_std(sc, std);
+ }
+}
+
+usbd_status
+uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len,
+ int rd, u_int16_t flags, usbd_xfer_handle xfer,
+ uhci_soft_td_t **sp, uhci_soft_td_t **ep)
+{
+ struct usb_dma_mapping *dma = &xfer->dmamap;
+ uhci_soft_td_t *p, *prevp, *startp;
+ int err, i, ntd, l, tog, maxp, seg, segoff;
+ u_int32_t status;
+ int addr = upipe->pipe.device->address;
+ int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+
+ DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d speed=%d "
+ "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len,
+ upipe->pipe.device->speed, flags));
+ maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize);
+ if (maxp == 0) {
+ printf("uhci_alloc_std_chain: maxp=0\n");
+ return (USBD_INVAL);
+ }
+ ntd = (len + maxp - 1) / maxp;
+ if (len == 0)
+ flags |= USBD_FORCE_SHORT_XFER;
+ if ((flags & USBD_FORCE_SHORT_XFER) && len % maxp == 0)
+ ntd++;
+ DPRINTFN(10, ("uhci_alloc_std_chain: maxp=%d ntd=%d\n", maxp, ntd));
+ KASSERT(ntd > 0, ("uhci_alloc_std_chain: ntd=0"));
+ tog = upipe->nexttoggle;
+ prevp = NULL;
+ startp = NULL;
+ status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE);
+ if (upipe->pipe.device->speed == USB_SPEED_LOW)
+ status |= UHCI_TD_LS;
+ if (flags & USBD_SHORT_XFER_OK)
+ status |= UHCI_TD_SPD;
+ seg = 0;
+ segoff = 0;
+ for (i = 0; i < ntd; i++) {
+ p = uhci_alloc_std(sc);
+ if (p == NULL) {
+ uhci_free_std_chain(sc, startp, NULL);
+ return (USBD_NOMEM);
+ }
+ p->link.std = NULL;
+ if (prevp != NULL) {
+ prevp->link.std = p;
+ prevp->td.td_link = htole32(p->physaddr | UHCI_PTR_VF |
+ UHCI_PTR_TD);
+ } else {
+ startp = p;
+ }
+ p->td.td_status = htole32(status);
+ if (i == ntd - 1) {
+ /* last TD */
+ l = len % maxp;
+ if (l == 0 && !(flags & USBD_FORCE_SHORT_XFER))
+ l = maxp;
+ *ep = p;
+ } else
+ l = maxp;
+ p->td.td_token =
+ htole32(rd ? UHCI_TD_IN (l, endpt, addr, tog) :
+ UHCI_TD_OUT(l, endpt, addr, tog));
+
+ KASSERT(seg < dma->nsegs || l == 0,
+ ("uhci_alloc_std_chain: too few segments"));
+ if (l == 0) {
+ p->td.td_buffer = 0;
+ } else if (l > dma->segs[seg].ds_len - segoff) {
+ /* UHCI can't handle non-contiguous data. */
+ err = uhci_aux_dma_alloc(sc, p, (char *)xfer->buffer +
+ i * maxp, l);
+ if (err) {
+ uhci_free_std_chain(sc, startp, NULL);
+ return (err);
+ }
+ p->td.td_buffer = htole32(uhci_aux_dma_prepare(p, rd));
+ l -= dma->segs[seg].ds_len - segoff;
+ seg++;
+ KASSERT(seg < dma->nsegs,
+ ("uhci_alloc_std_chain: too few segments 2"));
+ segoff = 0;
+ } else {
+ p->td.td_buffer = htole32(dma->segs[seg].ds_addr +
+ segoff);
+ }
+ segoff += l;
+ if (l > 0 && segoff >= dma->segs[seg].ds_len) {
+ KASSERT(segoff == dma->segs[seg].ds_len,
+ ("uhci_alloc_std_chain: overlap"));
+ if (i * maxp + l != len) {
+ seg++;
+ segoff = 0;
+ }
+ }
+ prevp = p;
+ tog ^= 1;
+ }
+ prevp->td.td_link = htole32(UHCI_PTR_T | UHCI_PTR_VF | UHCI_PTR_TD);
+ upipe->nexttoggle = tog;
+ *sp = startp;
+ DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n",
+ upipe->nexttoggle));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/*
+ * Allocate a physically contiguous buffer to handle cases where UHCI
+ * cannot handle a packet because it is not physically contiguous.
+ * If the usb_dma_t was already allocated this just ensures it is
+ * large enough for the specified size.
+ */
+static usbd_status
+uhci_aux_dma_alloc(uhci_softc_t *sc, uhci_soft_td_t *std, void *data, int len)
+{
+ int err, align;
+
+ if (std->aux_dma.block == NULL || std->aux_dma.block->size < len) {
+ /* Align to avoid crossing a page boundary. */
+ if (powerof2(len))
+ align = len;
+ else
+ align = 1 << fls(len);
+
+ if (std->aux_dma.block != NULL)
+ usb_freemem(&sc->sc_bus, &std->aux_dma);
+ std->aux_dma.block = NULL;
+ err = usb_allocmem(&sc->sc_bus, len, align, &std->aux_dma);
+ if (err)
+ return (err);
+ }
+ std->aux_data = data;
+ std->aux_len = len;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static uhci_physaddr_t
+uhci_aux_dma_prepare(uhci_soft_td_t *std, int isread)
+{
+ if (!isread) {
+ bcopy(std->aux_data, KERNADDR(&std->aux_dma, 0), std->aux_len);
+ bus_dmamap_sync(std->aux_dma.block->tag,
+ std->aux_dma.block->map, BUS_DMASYNC_PREWRITE);
+ }
+
+ return (DMAADDR(&std->aux_dma, 0));
+}
+
+static void
+uhci_aux_dma_complete(uhci_soft_td_t *std, int isread)
+{
+ if (isread) {
+ bus_dmamap_sync(std->aux_dma.block->tag,
+ std->aux_dma.block->map, BUS_DMASYNC_POSTREAD);
+ bcopy(KERNADDR(&std->aux_dma, 0), std->aux_data, std->aux_len);
+ }
+}
+
+void
+uhci_device_clear_toggle(usbd_pipe_handle pipe)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ upipe->nexttoggle = 0;
+}
+
+void
+uhci_noop(usbd_pipe_handle pipe)
+{
+}
+
+usbd_status
+uhci_device_bulk_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_device_bulk_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_device_bulk_start(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *data, *dataend;
+ uhci_soft_qh_t *sqh;
+ usbd_status err;
+ int len, isread, endpt;
+ int s;
+
+ DPRINTFN(3, ("uhci_device_bulk_start: xfer=%p len=%d flags=%d ii=%p\n",
+ xfer, xfer->length, xfer->flags, ii));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("uhci_device_bulk_transfer: a request");
+#endif
+
+ len = xfer->length;
+ endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sqh = upipe->u.bulk.sqh;
+
+ upipe->u.bulk.isread = isread;
+ upipe->u.bulk.length = len;
+
+ err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags, xfer,
+ &data, &dataend);
+ if (err)
+ return (err);
+ dataend->td.td_status |= htole32(UHCI_TD_IOC);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 8) {
+ DPRINTF(("uhci_device_bulk_transfer: data(1)\n"));
+ uhci_dump_tds(data);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = data;
+ ii->stdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_bulk_transfer: not done, ii=%p\n", ii);
+ }
+ ii->isdone = 0;
+#endif
+
+ sqh->elink = data;
+ sqh->qh.qh_elink = htole32(data->physaddr | UHCI_PTR_TD);
+
+ s = splusb();
+ uhci_add_bulk(sc, sqh);
+ uhci_add_intr_info(sc, ii);
+
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ uhci_timeout, ii);
+ }
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_bulk_transfer: data(2)\n"));
+ uhci_dump_tds(data);
+ }
+#endif
+
+ if (sc->sc_bus.use_polling)
+ uhci_waitintr(sc, xfer);
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a device bulk request. */
+void
+uhci_device_bulk_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("uhci_device_bulk_abort:\n"));
+ uhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/*
+ * Abort a device request.
+ * If this routine is called at splusb() it guarantees that the request
+ * will be removed from the hardware scheduling and that the callback
+ * for it will be called with USBD_CANCELLED status.
+ * It's impossible to guarantee that the requested transfer will not
+ * have happened since the hardware runs concurrently.
+ * If the transaction has already happened we rely on the ordinary
+ * interrupt processing to process it.
+ */
+void
+uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+ struct uhci_xfer *uxfer = UXFER(xfer);
+ uhci_intr_info_t *ii = &uxfer->iinfo;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
+ uhci_soft_td_t *std;
+ int s;
+
+ DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));
+
+ if (sc->sc_dying) {
+ /* If we're dying, just do the software part. */
+ s = splusb();
+ xfer->status = status; /* make software ignore it */
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
+ uhci_transfer_complete(xfer);
+ splx(s);
+ return;
+ }
+
+ if (xfer->device->bus->intr_context || !curproc)
+ panic("uhci_abort_xfer: not in process context");
+
+ /*
+ * If an abort is already in progress then just wait for it to
+ * complete and return.
+ */
+ if (uxfer->uhci_xfer_flags & UHCI_XFER_ABORTING) {
+ DPRINTFN(2, ("uhci_abort_xfer: already aborting\n"));
+ /* No need to wait if we're aborting from a timeout. */
+ if (status == USBD_TIMEOUT)
+ return;
+ /* Override the status which might be USBD_TIMEOUT. */
+ xfer->status = status;
+ DPRINTFN(2, ("uhci_abort_xfer: waiting for abort to finish\n"));
+ uxfer->uhci_xfer_flags |= UHCI_XFER_ABORTWAIT;
+ while (uxfer->uhci_xfer_flags & UHCI_XFER_ABORTING)
+ tsleep(&uxfer->uhci_xfer_flags, PZERO, "uhciaw", 0);
+ return;
+ }
+
+ /*
+ * Step 1: Make interrupt routine and hardware ignore xfer.
+ */
+ s = splusb();
+ uxfer->uhci_xfer_flags |= UHCI_XFER_ABORTING;
+ xfer->status = status; /* make software ignore it */
+ callout_stop(&xfer->timeout_handle);
+ usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
+ DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
+ for (std = ii->stdstart; std != NULL; std = std->link.std)
+ std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
+ splx(s);
+
+ /*
+ * Step 2: Wait until we know hardware has finished any possible
+ * use of the xfer. Also make sure the soft interrupt routine
+ * has run.
+ */
+ usb_delay_ms(upipe->pipe.device->bus, 2); /* Hardware finishes in 1ms */
+ s = splusb();
+#ifdef USB_USE_SOFTINTR
+ sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+ usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+ DPRINTFN(1,("uhci_abort_xfer: tsleep\n"));
+ tsleep(&sc->sc_softwake, PZERO, "uhciab", 0);
+#endif /* USB_USE_SOFTINTR */
+ splx(s);
+
+ /*
+ * Step 3: Execute callback.
+ */
+ DPRINTFN(1,("uhci_abort_xfer: callback\n"));
+ s = splusb();
+#ifdef DIAGNOSTIC
+ ii->isdone = 1;
+#endif
+ /* Do the wakeup first to avoid touching the xfer after the callback. */
+ uxfer->uhci_xfer_flags &= ~UHCI_XFER_ABORTING;
+ if (uxfer->uhci_xfer_flags & UHCI_XFER_ABORTWAIT) {
+ uxfer->uhci_xfer_flags &= ~UHCI_XFER_ABORTWAIT;
+ wakeup(&uxfer->uhci_xfer_flags);
+ }
+ uhci_transfer_complete(xfer);
+ splx(s);
+}
+
+/*
+ * Perform any UHCI-specific transfer completion operations, then
+ * call usb_transfer_complete().
+ */
+static void
+uhci_transfer_complete(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_td_t *p;
+ int i, isread, n;
+
+ /* XXX, must be an easier way to detect reads... */
+ isread = ((xfer->rqflags & URQ_REQUEST) &&
+ (xfer->request.bmRequestType & UT_READ)) ||
+ (xfer->pipe->endpoint->edesc->bEndpointAddress & UE_DIR_IN);
+
+ /* Copy back from any auxillary buffers after a read operation. */
+ if (xfer->nframes == 0) {
+ for (p = ii->stdstart; p != NULL; p = p->link.std) {
+ if (p->aux_data != NULL)
+ uhci_aux_dma_complete(p, isread);
+ }
+ } else {
+ if (xfer->nframes != 0) {
+ /* Isoc transfer, do things differently. */
+ n = UXFER(xfer)->curframe;
+ for (i = 0; i < xfer->nframes; i++) {
+ p = upipe->u.iso.stds[n];
+ if (p->aux_data != NULL)
+ uhci_aux_dma_complete(p, isread);
+ if (++n >= UHCI_VFRAMELIST_COUNT)
+ n = 0;
+ }
+ }
+ }
+
+ usb_transfer_complete(xfer);
+}
+
+/* Close a device bulk pipe. */
+void
+uhci_device_bulk_close(usbd_pipe_handle pipe)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+
+ uhci_free_sqh(sc, upipe->u.bulk.sqh);
+ pipe->endpoint->savedtoggle = upipe->nexttoggle;
+}
+
+usbd_status
+uhci_device_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_device_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_device_ctrl_start(usbd_xfer_handle xfer)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ panic("uhci_device_ctrl_transfer: not a request");
+#endif
+
+ err = uhci_device_request(xfer);
+ if (err)
+ return (err);
+
+ if (sc->sc_bus.use_polling)
+ uhci_waitintr(sc, xfer);
+ return (USBD_IN_PROGRESS);
+}
+
+usbd_status
+uhci_device_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_device_intr_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_device_intr_start(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *data, *dataend;
+ uhci_soft_qh_t *sqh;
+ usbd_status err;
+ int isread, endpt;
+ int i, s;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ DPRINTFN(3,("uhci_device_intr_transfer: xfer=%p len=%d flags=%d\n",
+ xfer, xfer->length, xfer->flags));
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_REQUEST)
+ panic("uhci_device_intr_transfer: a request");
+#endif
+
+ endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ isread = UE_GET_DIR(endpt) == UE_DIR_IN;
+ sqh = upipe->u.bulk.sqh;
+
+ upipe->u.intr.isread = isread;
+
+ err = uhci_alloc_std_chain(upipe, sc, xfer->length, isread, xfer->flags,
+ xfer, &data, &dataend);
+ if (err)
+ return (err);
+ dataend->td.td_status |= htole32(UHCI_TD_IOC);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_intr_transfer: data(1)\n"));
+ uhci_dump_tds(data);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ s = splusb();
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = data;
+ ii->stdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_intr_transfer: not done, ii=%p\n", ii);
+ }
+ ii->isdone = 0;
+#endif
+
+ DPRINTFN(10,("uhci_device_intr_transfer: qhs[0]=%p\n",
+ upipe->u.intr.qhs[0]));
+ for (i = 0; i < upipe->u.intr.npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->elink = data;
+ sqh->qh.qh_elink = htole32(data->physaddr | UHCI_PTR_TD);
+ }
+ uhci_add_intr_info(sc, ii);
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_intr_transfer: data(2)\n"));
+ uhci_dump_tds(data);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a device control request. */
+void
+uhci_device_ctrl_abort(usbd_xfer_handle xfer)
+{
+ DPRINTF(("uhci_device_ctrl_abort:\n"));
+ uhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device control pipe. */
+void
+uhci_device_ctrl_close(usbd_pipe_handle pipe)
+{
+}
+
+/* Abort a device interrupt request. */
+void
+uhci_device_intr_abort(usbd_xfer_handle xfer)
+{
+ DPRINTFN(1,("uhci_device_intr_abort: xfer=%p\n", xfer));
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTFN(1,("uhci_device_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = NULL;
+ }
+ uhci_abort_xfer(xfer, USBD_CANCELLED);
+}
+
+/* Close a device interrupt pipe. */
+void
+uhci_device_intr_close(usbd_pipe_handle pipe)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ int i, npoll;
+ int s;
+
+ /* Unlink descriptors from controller data structures. */
+ npoll = upipe->u.intr.npoll;
+ s = splusb();
+ for (i = 0; i < npoll; i++)
+ uhci_remove_intr(sc, upipe->u.intr.qhs[i]);
+ splx(s);
+
+ /*
+ * We now have to wait for any activity on the physical
+ * descriptors to stop.
+ */
+ usb_delay_ms(&sc->sc_bus, 2);
+
+ for(i = 0; i < npoll; i++)
+ uhci_free_sqh(sc, upipe->u.intr.qhs[i]);
+ free(upipe->u.intr.qhs, M_USBHC);
+
+ /* XXX free other resources */
+}
+
+usbd_status
+uhci_device_request(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usb_device_request_t *req = &xfer->request;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ int addr = dev->address;
+ int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *setup, *data, *stat, *next, *dataend;
+ uhci_soft_qh_t *sqh;
+ int len;
+ u_int32_t ls;
+ usbd_status err;
+ int isread;
+ int s;
+
+ DPRINTFN(3,("uhci_device_control type=0x%02x, request=0x%02x, "
+ "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n",
+ req->bmRequestType, req->bRequest, UGETW(req->wValue),
+ UGETW(req->wIndex), UGETW(req->wLength),
+ addr, endpt));
+
+ ls = dev->speed == USB_SPEED_LOW ? UHCI_TD_LS : 0;
+ isread = req->bmRequestType & UT_READ;
+ len = UGETW(req->wLength);
+
+ setup = upipe->u.ctl.setup;
+ stat = upipe->u.ctl.stat;
+ sqh = upipe->u.ctl.sqh;
+
+ /* Set up data transaction */
+ if (len != 0) {
+ upipe->nexttoggle = 1;
+ err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags,
+ xfer, &data, &dataend);
+ if (err)
+ return (err);
+ next = data;
+ dataend->link.std = stat;
+ dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_VF | UHCI_PTR_TD);
+ } else {
+ next = stat;
+ }
+ upipe->u.ctl.length = len;
+
+ memcpy(KERNADDR(&upipe->u.ctl.reqdma, 0), req, sizeof *req);
+
+ setup->link.std = next;
+ setup->td.td_link = htole32(next->physaddr | UHCI_PTR_VF | UHCI_PTR_TD);
+ setup->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
+ UHCI_TD_ACTIVE);
+ setup->td.td_token = htole32(UHCI_TD_SETUP(sizeof *req, endpt, addr));
+ setup->td.td_buffer = htole32(DMAADDR(&upipe->u.ctl.reqdma, 0));
+
+ stat->link.std = NULL;
+ stat->td.td_link = htole32(UHCI_PTR_T);
+ stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls |
+ UHCI_TD_ACTIVE | UHCI_TD_IOC);
+ stat->td.td_token =
+ htole32(isread ? UHCI_TD_OUT(0, endpt, addr, 1) :
+ UHCI_TD_IN (0, endpt, addr, 1));
+ stat->td.td_buffer = htole32(0);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_request: before transfer\n"));
+ uhci_dump_tds(setup);
+ }
+#endif
+
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = setup;
+ ii->stdend = stat;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_request: not done, ii=%p\n", ii);
+ }
+ ii->isdone = 0;
+#endif
+
+ sqh->elink = setup;
+ sqh->qh.qh_elink = htole32(setup->physaddr | UHCI_PTR_TD);
+
+ s = splusb();
+ if (dev->speed == USB_SPEED_LOW)
+ uhci_add_ls_ctrl(sc, sqh);
+ else
+ uhci_add_hs_ctrl(sc, sqh);
+ uhci_add_intr_info(sc, ii);
+#ifdef USB_DEBUG
+ if (uhcidebug > 12) {
+ uhci_soft_td_t *std;
+ uhci_soft_qh_t *xqh;
+ uhci_soft_qh_t *sxqh;
+ int maxqh = 0;
+ uhci_physaddr_t link;
+ DPRINTF(("uhci_enter_ctl_q: follow from [0]\n"));
+ for (std = sc->sc_vframes[0].htd, link = 0;
+ (link & UHCI_PTR_QH) == 0;
+ std = std->link.std) {
+ link = le32toh(std->td.td_link);
+ uhci_dump_td(std);
+ }
+ sxqh = (uhci_soft_qh_t *)std;
+ uhci_dump_qh(sxqh);
+ for (xqh = sxqh;
+ xqh != NULL;
+ xqh = (maxqh++ == 5 || xqh->hlink == sxqh ||
+ xqh->hlink == xqh ? NULL : xqh->hlink)) {
+ uhci_dump_qh(xqh);
+ }
+ DPRINTF(("Enqueued QH:\n"));
+ uhci_dump_qh(sqh);
+ uhci_dump_tds(sqh->elink);
+ }
+#endif
+ if (xfer->timeout && !sc->sc_bus.use_polling) {
+ callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
+ uhci_timeout, ii);
+ }
+ xfer->status = USBD_IN_PROGRESS;
+ splx(s);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+uhci_device_isoc_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ DPRINTFN(5,("uhci_device_isoc_transfer: xfer=%p\n", xfer));
+
+ /* Put it on our queue, */
+ err = usb_insert_transfer(xfer);
+
+ /* bail out on error, */
+ if (err && err != USBD_IN_PROGRESS)
+ return (err);
+
+ /* XXX should check inuse here */
+
+ /* insert into schedule, */
+ uhci_device_isoc_enter(xfer);
+
+ /* and start if the pipe wasn't running */
+ if (!err)
+ uhci_device_isoc_start(STAILQ_FIRST(&xfer->pipe->queue));
+
+ return (err);
+}
+
+void
+uhci_device_isoc_enter(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ struct iso *iso = &upipe->u.iso;
+ uhci_soft_td_t *std;
+ void *dataptr;
+ u_int32_t len, status;
+ int err, s, i, isread, next, nframes, seg, segoff;
+
+ DPRINTFN(5,("uhci_device_isoc_enter: used=%d next=%d xfer=%p "
+ "nframes=%d\n",
+ iso->inuse, iso->next, xfer, xfer->nframes));
+
+ if (sc->sc_dying)
+ return;
+
+ if (xfer->status == USBD_IN_PROGRESS) {
+ /* This request has already been entered into the frame list */
+ printf("uhci_device_isoc_enter: xfer=%p in frame list\n", xfer);
+ /* XXX */
+ }
+
+#ifdef DIAGNOSTIC
+ if (iso->inuse >= UHCI_VFRAMELIST_COUNT)
+ printf("uhci_device_isoc_enter: overflow!\n");
+#endif
+
+ next = iso->next;
+ if (next == -1) {
+ /* Not in use yet, schedule it a few frames ahead. */
+ next = (UREAD2(sc, UHCI_FRNUM) + 3) % UHCI_VFRAMELIST_COUNT;
+ DPRINTFN(2,("uhci_device_isoc_enter: start next=%d\n", next));
+ }
+
+ xfer->status = USBD_IN_PROGRESS;
+ UXFER(xfer)->curframe = next;
+
+ seg = 0;
+ segoff = 0;
+ dataptr = xfer->allocbuf; /* Normal buffers not possible for isoc? */
+ isread = xfer->pipe->endpoint->edesc->bEndpointAddress & UE_DIR_IN;
+ status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) |
+ UHCI_TD_ACTIVE |
+ UHCI_TD_IOS);
+ nframes = xfer->nframes;
+ s = splusb();
+ for (i = 0; i < nframes; i++) {
+ std = iso->stds[next];
+ if (++next >= UHCI_VFRAMELIST_COUNT)
+ next = 0;
+ len = xfer->frlengths[i];
+ KASSERT(seg < xfer->dmamap.nsegs,
+ ("uhci_device_isoc_enter: too few segments"));
+ if (len + segoff > xfer->dmamap.segs[seg].ds_len) {
+ /* UHCI can't handle non-contiguous data. */
+ err = uhci_aux_dma_alloc(sc, std, dataptr, len);
+ /* XXX */
+ if (err)
+ printf("uhci_device_isoc_enter: aux alloc\n");
+ std->td.td_buffer = htole32(uhci_aux_dma_prepare(std,
+ isread));
+ segoff += len;
+ while (segoff >= xfer->dmamap.segs[seg].ds_len) {
+ KASSERT(seg < xfer->dmamap.nsegs - 1 ||
+ segoff == xfer->dmamap.segs[seg].ds_len,
+ ("uhci_device_isoc_enter: overlap2"));
+ segoff -= xfer->dmamap.segs[seg].ds_len;
+ seg++;
+ }
+ } else {
+ std->td.td_buffer =
+ htole32(xfer->dmamap.segs[seg].ds_addr + segoff);
+ segoff += len;
+ if (segoff >= xfer->dmamap.segs[seg].ds_len) {
+ KASSERT(segoff == xfer->dmamap.segs[seg].ds_len,
+ ("uhci_device_isoc_enter: overlap"));
+ segoff = 0;
+ seg++;
+ }
+ }
+ if (i == nframes - 1)
+ status |= UHCI_TD_IOC;
+ std->td.td_status = htole32(status);
+ std->td.td_token &= htole32(~UHCI_TD_MAXLEN_MASK);
+ std->td.td_token |= htole32(UHCI_TD_SET_MAXLEN(len));
+#ifdef USB_DEBUG
+ if (uhcidebug > 5) {
+ DPRINTFN(5,("uhci_device_isoc_enter: TD %d\n", i));
+ uhci_dump_td(std);
+ }
+#endif
+ dataptr = (char *)dataptr + len;
+ }
+ iso->next = next;
+ iso->inuse += xfer->nframes;
+
+ splx(s);
+}
+
+usbd_status
+uhci_device_isoc_start(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_soft_td_t *end;
+ int s, i;
+
+ DPRINTFN(5,("uhci_device_isoc_start: xfer=%p\n", xfer));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (xfer->status != USBD_IN_PROGRESS)
+ printf("uhci_device_isoc_start: not in progress %p\n", xfer);
+#endif
+
+ /* Find the last TD */
+ i = UXFER(xfer)->curframe + xfer->nframes;
+ if (i >= UHCI_VFRAMELIST_COUNT)
+ i -= UHCI_VFRAMELIST_COUNT;
+ end = upipe->u.iso.stds[i];
+
+#ifdef DIAGNOSTIC
+ if (end == NULL) {
+ printf("uhci_device_isoc_start: end == NULL\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ s = splusb();
+
+ /* Set up interrupt info. */
+ ii->xfer = xfer;
+ ii->stdstart = end;
+ ii->stdend = end;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone)
+ printf("uhci_device_isoc_start: not done, ii=%p\n", ii);
+ ii->isdone = 0;
+#endif
+ uhci_add_intr_info(sc, ii);
+
+ splx(s);
+
+ return (USBD_IN_PROGRESS);
+}
+
+void
+uhci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_td_t **stds = upipe->u.iso.stds;
+ uhci_soft_td_t *std;
+ int i, n, s, nframes, maxlen, len;
+
+ s = splusb();
+
+ /* Transfer is already done. */
+ if (xfer->status != USBD_NOT_STARTED &&
+ xfer->status != USBD_IN_PROGRESS) {
+ splx(s);
+ return;
+ }
+
+ /* Give xfer the requested abort code. */
+ xfer->status = USBD_CANCELLED;
+
+ /* make hardware ignore it, */
+ nframes = xfer->nframes;
+ n = UXFER(xfer)->curframe;
+ maxlen = 0;
+ for (i = 0; i < nframes; i++) {
+ std = stds[n];
+ std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
+ len = UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token));
+ if (len > maxlen)
+ maxlen = len;
+ if (++n >= UHCI_VFRAMELIST_COUNT)
+ n = 0;
+ }
+
+ /* and wait until we are sure the hardware has finished. */
+ delay(maxlen);
+
+#ifdef DIAGNOSTIC
+ UXFER(xfer)->iinfo.isdone = 1;
+#endif
+ /* Run callback and remove from interrupt list. */
+ uhci_transfer_complete(xfer);
+
+ splx(s);
+}
+
+void
+uhci_device_isoc_close(usbd_pipe_handle pipe)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ uhci_soft_td_t *std, *vstd;
+ struct iso *iso;
+ int i, s;
+
+ /*
+ * Make sure all TDs are marked as inactive.
+ * Wait for completion.
+ * Unschedule.
+ * Deallocate.
+ */
+ iso = &upipe->u.iso;
+
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++)
+ iso->stds[i]->td.td_status &= htole32(~UHCI_TD_ACTIVE);
+ usb_delay_ms(&sc->sc_bus, 2); /* wait for completion */
+
+ s = splusb();
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = iso->stds[i];
+ for (vstd = sc->sc_vframes[i].htd;
+ vstd != NULL && vstd->link.std != std;
+ vstd = vstd->link.std)
+ ;
+ if (vstd == NULL) {
+ /*panic*/
+ printf("uhci_device_isoc_close: %p not found\n", std);
+ splx(s);
+ return;
+ }
+ vstd->link = std->link;
+ vstd->td.td_link = std->td.td_link;
+ uhci_free_std(sc, std);
+ }
+ splx(s);
+
+ free(iso->stds, M_USBHC);
+}
+
+usbd_status
+uhci_setup_isoc(usbd_pipe_handle pipe)
+{
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ usbd_device_handle dev = upipe->pipe.device;
+ uhci_softc_t *sc = (uhci_softc_t *)dev->bus;
+ int addr = upipe->pipe.device->address;
+ int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress;
+ int rd = UE_GET_DIR(endpt) == UE_DIR_IN;
+ uhci_soft_td_t *std, *vstd;
+ u_int32_t token;
+ struct iso *iso;
+ int i, s;
+
+ iso = &upipe->u.iso;
+ iso->stds = malloc(UHCI_VFRAMELIST_COUNT * sizeof (uhci_soft_td_t *),
+ M_USBHC, M_WAITOK);
+
+ token = rd ? UHCI_TD_IN (0, endpt, addr, 0) :
+ UHCI_TD_OUT(0, endpt, addr, 0);
+
+ /* Allocate the TDs and mark as inactive; */
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = uhci_alloc_std(sc);
+ if (std == 0)
+ goto bad;
+ std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */
+ std->td.td_token = htole32(token);
+ iso->stds[i] = std;
+ }
+
+ /* Insert TDs into schedule. */
+ s = splusb();
+ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {
+ std = iso->stds[i];
+ vstd = sc->sc_vframes[i].htd;
+ std->link = vstd->link;
+ std->td.td_link = vstd->td.td_link;
+ vstd->link.std = std;
+ vstd->td.td_link = htole32(std->physaddr | UHCI_PTR_TD);
+ }
+ splx(s);
+
+ iso->next = -1;
+ iso->inuse = 0;
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ while (--i >= 0)
+ uhci_free_std(sc, iso->stds[i]);
+ free(iso->stds, M_USBHC);
+ return (USBD_NOMEM);
+}
+
+void
+uhci_device_isoc_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+
+ DPRINTFN(4, ("uhci_isoc_done: length=%d\n", xfer->actlen));
+
+ if (ii->xfer != xfer)
+ /* Not on interrupt list, ignore it. */
+ return;
+
+ if (!uhci_active_intr_info(ii))
+ return;
+
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("uhci_device_isoc_done: xfer=%p not busy 0x%08x\n",
+ xfer, xfer->busy_free);
+ return;
+ }
+
+ if (ii->stdend == NULL) {
+ printf("uhci_device_isoc_done: xfer=%p stdend==NULL\n", xfer);
+#ifdef USB_DEBUG
+ uhci_dump_ii(ii);
+#endif
+ return;
+ }
+#endif
+
+ /* Turn off the interrupt since it is active even if the TD is not. */
+ ii->stdend->td.td_status &= htole32(~UHCI_TD_IOC);
+
+ uhci_del_intr_info(ii); /* remove from active list */
+
+#ifdef DIAGNOSTIC
+ if (ii->stdend == NULL) {
+ printf("uhci_device_isoc_done: xfer=%p stdend==NULL\n", xfer);
+#ifdef USB_DEBUG
+ uhci_dump_ii(ii);
+#endif
+ return;
+ }
+#endif
+ ii->stdstart = NULL;
+ ii->stdend = NULL;
+}
+
+void
+uhci_device_intr_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_softc_t *sc = ii->sc;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+ uhci_soft_qh_t *sqh;
+ int i, npoll;
+
+ DPRINTFN(5, ("uhci_device_intr_done: length=%d\n", xfer->actlen));
+
+ npoll = upipe->u.intr.npoll;
+ for(i = 0; i < npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->elink = NULL;
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ }
+ uhci_free_std_chain(sc, ii->stdstart, NULL);
+
+ /* XXX Wasteful. */
+ if (xfer->pipe->repeat) {
+ uhci_soft_td_t *data, *dataend;
+
+ DPRINTFN(5,("uhci_device_intr_done: requeing\n"));
+
+ /* This alloc cannot fail since we freed the chain above. */
+ uhci_alloc_std_chain(upipe, sc, xfer->length,
+ upipe->u.intr.isread, xfer->flags, xfer,
+ &data, &dataend);
+ dataend->td.td_status |= htole32(UHCI_TD_IOC);
+
+#ifdef USB_DEBUG
+ if (uhcidebug > 10) {
+ DPRINTF(("uhci_device_intr_done: data(1)\n"));
+ uhci_dump_tds(data);
+ uhci_dump_qh(upipe->u.intr.qhs[0]);
+ }
+#endif
+
+ ii->stdstart = data;
+ ii->stdend = dataend;
+#ifdef DIAGNOSTIC
+ if (!ii->isdone) {
+ printf("uhci_device_intr_done: not done, ii=%p\n", ii);
+ }
+ ii->isdone = 0;
+#endif
+ for (i = 0; i < npoll; i++) {
+ sqh = upipe->u.intr.qhs[i];
+ sqh->elink = data;
+ sqh->qh.qh_elink = htole32(data->physaddr | UHCI_PTR_TD);
+ }
+ xfer->status = USBD_IN_PROGRESS;
+ /* The ii is already on the examined list, just leave it. */
+ } else {
+ DPRINTFN(5,("uhci_device_intr_done: removing\n"));
+ if (uhci_active_intr_info(ii)) {
+ uhci_del_intr_info(ii);
+ ii->stdstart = NULL;
+ ii->stdend = NULL;
+ }
+ }
+}
+
+/* Deallocate request data structures */
+void
+uhci_device_ctrl_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_softc_t *sc = ii->sc;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ panic("uhci_device_ctrl_done: not a request");
+#endif
+
+ if (!uhci_active_intr_info(ii))
+ return;
+
+ uhci_del_intr_info(ii); /* remove from active list */
+
+ if (upipe->pipe.device->speed == USB_SPEED_LOW)
+ uhci_remove_ls_ctrl(sc, upipe->u.ctl.sqh);
+ else
+ uhci_remove_hs_ctrl(sc, upipe->u.ctl.sqh);
+
+ if (upipe->u.ctl.length != 0)
+ uhci_free_std_chain(sc, ii->stdstart->link.std, ii->stdend);
+ ii->stdstart = NULL;
+ ii->stdend = NULL;
+
+ DPRINTFN(5, ("uhci_device_ctrl_done: length=%d\n", xfer->actlen));
+}
+
+/* Deallocate request data structures */
+void
+uhci_device_bulk_done(usbd_xfer_handle xfer)
+{
+ uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
+ uhci_softc_t *sc = ii->sc;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe;
+
+ DPRINTFN(5,("uhci_device_bulk_done: xfer=%p ii=%p sc=%p upipe=%p\n",
+ xfer, ii, sc, upipe));
+
+ if (!uhci_active_intr_info(ii))
+ return;
+
+ uhci_del_intr_info(ii); /* remove from active list */
+
+ uhci_remove_bulk(sc, upipe->u.bulk.sqh);
+
+ uhci_free_std_chain(sc, ii->stdstart, NULL);
+ ii->stdstart = NULL;
+ ii->stdend = NULL;
+
+ DPRINTFN(5, ("uhci_device_bulk_done: length=%d\n", xfer->actlen));
+}
+
+/* Add interrupt QH, called with vflock. */
+void
+uhci_add_intr(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos];
+ uhci_soft_qh_t *eqh;
+
+ DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", sqh->pos, sqh));
+
+ eqh = vf->eqh;
+ sqh->hlink = eqh->hlink;
+ sqh->qh.qh_hlink = eqh->qh.qh_hlink;
+ eqh->hlink = sqh;
+ eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH);
+ vf->eqh = sqh;
+ vf->bandwidth++;
+}
+
+/* Remove interrupt QH. */
+void
+uhci_remove_intr(uhci_softc_t *sc, uhci_soft_qh_t *sqh)
+{
+ struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos];
+ uhci_soft_qh_t *pqh;
+
+ DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", sqh->pos, sqh));
+
+ /* See comment in uhci_remove_ctrl() */
+ if (!(sqh->qh.qh_elink & htole32(UHCI_PTR_T))) {
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ delay(UHCI_QH_REMOVE_DELAY);
+ }
+
+ pqh = uhci_find_prev_qh(vf->hqh, sqh);
+ pqh->hlink = sqh->hlink;
+ pqh->qh.qh_hlink = sqh->qh.qh_hlink;
+ delay(UHCI_QH_REMOVE_DELAY);
+ if (vf->eqh == sqh)
+ vf->eqh = pqh;
+ vf->bandwidth--;
+}
+
+usbd_status
+uhci_device_setintr(uhci_softc_t *sc, struct uhci_pipe *upipe, int ival)
+{
+ uhci_soft_qh_t *sqh;
+ int i, npoll, s;
+ u_int bestbw, bw, bestoffs, offs;
+
+ DPRINTFN(2, ("uhci_device_setintr: pipe=%p\n", upipe));
+ if (ival == 0) {
+ printf("uhci_setintr: 0 interval\n");
+ return (USBD_INVAL);
+ }
+
+ if (ival > UHCI_VFRAMELIST_COUNT)
+ ival = UHCI_VFRAMELIST_COUNT;
+ npoll = (UHCI_VFRAMELIST_COUNT + ival - 1) / ival;
+ DPRINTFN(2, ("uhci_device_setintr: ival=%d npoll=%d\n", ival, npoll));
+
+ upipe->u.intr.npoll = npoll;
+ upipe->u.intr.qhs =
+ malloc(npoll * sizeof(uhci_soft_qh_t *), M_USBHC, M_WAITOK);
+
+ /*
+ * Figure out which offset in the schedule that has most
+ * bandwidth left over.
+ */
+#define MOD(i) ((i) & (UHCI_VFRAMELIST_COUNT-1))
+ for (bestoffs = offs = 0, bestbw = ~0; offs < ival; offs++) {
+ for (bw = i = 0; i < npoll; i++)
+ bw += sc->sc_vframes[MOD(i * ival + offs)].bandwidth;
+ if (bw < bestbw) {
+ bestbw = bw;
+ bestoffs = offs;
+ }
+ }
+ DPRINTFN(1, ("uhci_device_setintr: bw=%d offs=%d\n", bestbw, bestoffs));
+
+ for(i = 0; i < npoll; i++) {
+ upipe->u.intr.qhs[i] = sqh = uhci_alloc_sqh(sc);
+ sqh->elink = NULL;
+ sqh->qh.qh_elink = htole32(UHCI_PTR_T);
+ sqh->pos = MOD(i * ival + bestoffs);
+ }
+#undef MOD
+
+ s = splusb();
+ /* Enter QHs into the controller data structures. */
+ for(i = 0; i < npoll; i++)
+ uhci_add_intr(sc, upipe->u.intr.qhs[i]);
+ splx(s);
+
+ DPRINTFN(5, ("uhci_device_setintr: returns %p\n", upipe));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Open a new pipe. */
+usbd_status
+uhci_open(usbd_pipe_handle pipe)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+ struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
+ usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
+ usbd_status err;
+ int ival;
+
+ DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
+ pipe, pipe->device->address,
+ ed->bEndpointAddress, sc->sc_addr));
+
+ upipe->aborting = 0;
+ upipe->nexttoggle = pipe->endpoint->savedtoggle;
+
+ if (pipe->device->address == sc->sc_addr) {
+ switch (ed->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &uhci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | UHCI_INTR_ENDPT:
+ pipe->methods = &uhci_root_intr_methods;
+ break;
+ default:
+ return (USBD_INVAL);
+ }
+ } else {
+ switch (ed->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &uhci_device_ctrl_methods;
+ upipe->u.ctl.sqh = uhci_alloc_sqh(sc);
+ if (upipe->u.ctl.sqh == NULL)
+ goto bad;
+ upipe->u.ctl.setup = uhci_alloc_std(sc);
+ if (upipe->u.ctl.setup == NULL) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ goto bad;
+ }
+ upipe->u.ctl.stat = uhci_alloc_std(sc);
+ if (upipe->u.ctl.stat == NULL) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ uhci_free_std(sc, upipe->u.ctl.setup);
+ goto bad;
+ }
+ err = usb_allocmem(&sc->sc_bus,
+ sizeof(usb_device_request_t),
+ 0, &upipe->u.ctl.reqdma);
+ if (err) {
+ uhci_free_sqh(sc, upipe->u.ctl.sqh);
+ uhci_free_std(sc, upipe->u.ctl.setup);
+ uhci_free_std(sc, upipe->u.ctl.stat);
+ goto bad;
+ }
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &uhci_device_intr_methods;
+ ival = pipe->interval;
+ if (ival == USBD_DEFAULT_INTERVAL)
+ ival = ed->bInterval;
+ return (uhci_device_setintr(sc, upipe, ival));
+ case UE_ISOCHRONOUS:
+ pipe->methods = &uhci_device_isoc_methods;
+ return (uhci_setup_isoc(pipe));
+ case UE_BULK:
+ pipe->methods = &uhci_device_bulk_methods;
+ upipe->u.bulk.sqh = uhci_alloc_sqh(sc);
+ if (upipe->u.bulk.sqh == NULL)
+ goto bad;
+ break;
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ return (USBD_NOMEM);
+}
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+usb_device_descriptor_t uhci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x01}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+usb_config_descriptor_t uhci_confd = {
+ USB_CONFIG_DESCRIPTOR_SIZE,
+ UDESC_CONFIG,
+ {USB_CONFIG_DESCRIPTOR_SIZE +
+ USB_INTERFACE_DESCRIPTOR_SIZE +
+ USB_ENDPOINT_DESCRIPTOR_SIZE},
+ 1,
+ 1,
+ 0,
+ UC_SELF_POWERED,
+ 0 /* max power */
+};
+
+usb_interface_descriptor_t uhci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_FSHUB,
+ 0
+};
+
+usb_endpoint_descriptor_t uhci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | UHCI_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8},
+ 255
+};
+
+usb_hub_descriptor_t uhci_hubd_piix = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 2,
+ { UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0 },
+ 50, /* power on to power good */
+ 0,
+ { 0x00 }, /* both ports are removable */
+};
+
+int
+uhci_str(usb_string_descriptor_t *p, int l, char *s)
+{
+ int i;
+
+ if (l == 0)
+ return (0);
+ p->bLength = 2 * strlen(s) + 2;
+ if (l == 1)
+ return (1);
+ p->bDescriptorType = UDESC_STRING;
+ l -= 2;
+ for (i = 0; s[i] && l > 1; i++, l -= 2)
+ USETW2(p->bString[i], 0, s[i]);
+ return (2*i+2);
+}
+
+/*
+ * The USB hub protocol requires that SET_FEATURE(PORT_RESET) also
+ * enables the port, and also states that SET_FEATURE(PORT_ENABLE)
+ * should not be used by the USB subsystem. As we cannot issue a
+ * SET_FEATURE(PORT_ENABLE) externally, we must ensure that the port
+ * will be enabled as part of the reset.
+ *
+ * On the VT83C572, the port cannot be successfully enabled until the
+ * outstanding "port enable change" and "connection status change"
+ * events have been reset.
+ */
+static usbd_status
+uhci_portreset(uhci_softc_t *sc, int index)
+{
+ int lim, port, x;
+
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else
+ return (USBD_IOERROR);
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PR);
+
+ usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY);
+
+ DPRINTFN(3,("uhci port %d reset, status0 = 0x%04x\n",
+ index, UREAD2(sc, port)));
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+
+ delay(100);
+
+ DPRINTFN(3,("uhci port %d reset, status1 = 0x%04x\n",
+ index, UREAD2(sc, port)));
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+
+ for (lim = 10; --lim > 0;) {
+ usb_delay_ms(&sc->sc_bus, USB_PORT_RESET_DELAY);
+
+ x = UREAD2(sc, port);
+
+ DPRINTFN(3,("uhci port %d iteration %u, status = 0x%04x\n",
+ index, lim, x));
+
+ if (!(x & UHCI_PORTSC_CCS)) {
+ /*
+ * No device is connected (or was disconnected
+ * during reset). Consider the port reset.
+ * The delay must be long enough to ensure on
+ * the initial iteration that the device
+ * connection will have been registered. 50ms
+ * appears to be sufficient, but 20ms is not.
+ */
+ DPRINTFN(3,("uhci port %d loop %u, device detached\n",
+ index, lim));
+ break;
+ }
+
+ if (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)) {
+ /*
+ * Port enabled changed and/or connection
+ * status changed were set. Reset either or
+ * both raised flags (by writing a 1 to that
+ * bit), and wait again for state to settle.
+ */
+ UWRITE2(sc, port, URWMASK(x) |
+ (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)));
+ continue;
+ }
+
+ if (x & UHCI_PORTSC_PE)
+ /* Port is enabled */
+ break;
+
+ UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE);
+ }
+
+ DPRINTFN(3,("uhci port %d reset, status2 = 0x%04x\n",
+ index, UREAD2(sc, port)));
+
+ if (lim <= 0) {
+ DPRINTFN(1,("uhci port %d reset timed out\n", index));
+ return (USBD_TIMEOUT);
+ }
+
+ sc->sc_isreset = 1;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/*
+ * Simulate a hardware hub by handling all the necessary requests.
+ */
+usbd_status
+uhci_root_ctrl_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_root_ctrl_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+usbd_status
+uhci_root_ctrl_start(usbd_xfer_handle xfer)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ int port, x;
+ int s, len, value, index, status, change, l, totlen = 0;
+ usb_port_status_t ps;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_REQUEST))
+ panic("uhci_root_ctrl_transfer: not a request");
+#endif
+ req = &xfer->request;
+
+ DPRINTFN(2,("uhci_root_ctrl_control type=0x%02x request=%02x\n",
+ req->bmRequestType, req->bRequest));
+
+ len = UGETW(req->wLength);
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ if (len != 0)
+ buf = xfer->buffer;
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch(C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ if (len > 0) {
+ *(u_int8_t *)buf = sc->sc_conf;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ DPRINTFN(2,("uhci_root_ctrl_control wValue=0x%04x\n", value));
+ switch(value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ USETW(uhci_devd.idVendor, sc->sc_id_vendor);
+ memcpy(buf, &uhci_devd, l);
+ break;
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &uhci_confd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &uhci_ifcd, l);
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &uhci_endpd, l);
+ break;
+ case UDESC_STRING:
+ if (len == 0)
+ break;
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ switch (value & 0xff) {
+ case 1: /* Vendor */
+ totlen = uhci_str(buf, len, sc->sc_vendor);
+ break;
+ case 2: /* Product */
+ totlen = uhci_str(buf, len, "UHCI root hub");
+ break;
+ }
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ if (len > 0) {
+ *(u_int8_t *)buf = 0;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED);
+ totlen = 2;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, 0);
+ totlen = 2;
+ }
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(3, ("uhci_root_ctrl_control: UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value));
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_POEDC);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_OCIC);
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ err = USBD_NORMAL_COMPLETION;
+ goto ret;
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_POWER:
+ case UHF_PORT_LOW_SPEED:
+ case UHF_C_PORT_SUSPEND:
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len > 0) {
+ *(u_int8_t *)buf =
+ (UREAD2(sc, port) & UHCI_PORTSC_LS) >>
+ UHCI_PORTSC_LS_SHIFT;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ l = min(len, USB_HUB_DESCRIPTOR_SIZE);
+ totlen = l;
+ memcpy(buf, &uhci_hubd_piix, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len);
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ x = UREAD2(sc, port);
+ status = change = 0;
+ if (x & UHCI_PORTSC_CCS)
+ status |= UPS_CURRENT_CONNECT_STATUS;
+ if (x & UHCI_PORTSC_CSC)
+ change |= UPS_C_CONNECT_STATUS;
+ if (x & UHCI_PORTSC_PE)
+ status |= UPS_PORT_ENABLED;
+ if (x & UHCI_PORTSC_POEDC)
+ change |= UPS_C_PORT_ENABLED;
+ if (x & UHCI_PORTSC_OCI)
+ status |= UPS_OVERCURRENT_INDICATOR;
+ if (x & UHCI_PORTSC_OCIC)
+ change |= UPS_C_OVERCURRENT_INDICATOR;
+ if (x & UHCI_PORTSC_SUSP)
+ status |= UPS_SUSPEND;
+ if (x & UHCI_PORTSC_LSDA)
+ status |= UPS_LOW_SPEED;
+ status |= UPS_PORT_POWER;
+ if (sc->sc_isreset)
+ change |= UPS_C_PORT_RESET;
+ USETW(ps.wPortStatus, status);
+ USETW(ps.wPortChange, change);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ switch(value) {
+ case UHF_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ err = uhci_portreset(sc, index);
+ goto ret;
+ case UHF_PORT_POWER:
+ /* Pretend we turned on power */
+ err = USBD_NORMAL_COMPLETION;
+ goto ret;
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_LOW_SPEED:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_RESET:
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ ret:
+ xfer->status = err;
+ s = splusb();
+ uhci_transfer_complete(xfer);
+ splx(s);
+ return (USBD_IN_PROGRESS);
+}
+
+/* Abort a root control request. */
+void
+uhci_root_ctrl_abort(usbd_xfer_handle xfer)
+{
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+/* Close the root pipe. */
+void
+uhci_root_ctrl_close(usbd_pipe_handle pipe)
+{
+ DPRINTF(("uhci_root_ctrl_close\n"));
+}
+
+/* Abort a root interrupt request. */
+void
+uhci_root_intr_abort(usbd_xfer_handle xfer)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus;
+
+ callout_stop(&sc->sc_poll_handle);
+ sc->sc_intr_xfer = NULL;
+
+ if (xfer->pipe->intrxfer == xfer) {
+ DPRINTF(("uhci_root_intr_abort: remove\n"));
+ xfer->pipe->intrxfer = 0;
+ }
+ xfer->status = USBD_CANCELLED;
+#ifdef DIAGNOSTIC
+ UXFER(xfer)->iinfo.isdone = 1;
+#endif
+ uhci_transfer_complete(xfer);
+}
+
+usbd_status
+uhci_root_intr_transfer(usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Insert last in queue. */
+ err = usb_insert_transfer(xfer);
+ if (err)
+ return (err);
+
+ /*
+ * Pipe isn't running (otherwise err would be USBD_INPROG),
+ * so start it first.
+ */
+ return (uhci_root_intr_start(STAILQ_FIRST(&xfer->pipe->queue)));
+}
+
+/* Start a transfer on the root interrupt pipe */
+usbd_status
+uhci_root_intr_start(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+
+ DPRINTFN(3, ("uhci_root_intr_start: xfer=%p len=%d flags=%d\n",
+ xfer, xfer->length, xfer->flags));
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ sc->sc_ival = MS_TO_TICKS(xfer->pipe->endpoint->edesc->bInterval);
+ callout_reset(&sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer);
+ sc->sc_intr_xfer = xfer;
+ return (USBD_IN_PROGRESS);
+}
+
+/* Close the root interrupt pipe. */
+void
+uhci_root_intr_close(usbd_pipe_handle pipe)
+{
+ uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;
+
+ callout_stop(&sc->sc_poll_handle);
+ sc->sc_intr_xfer = NULL;
+ DPRINTF(("uhci_root_intr_close\n"));
+}
diff --git a/sys/legacy/dev/usb/uhci_pci.c b/sys/legacy/dev/usb/uhci_pci.c
new file mode 100644
index 0000000..4ee19d6
--- /dev/null
+++ b/sys/legacy/dev/usb/uhci_pci.c
@@ -0,0 +1,524 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Universal Host Controller Interface
+ *
+ * UHCI spec: http://www.intel.com/
+ */
+
+/* The low level controller code for UHCI has been split into
+ * PCI probes and UHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dev/usb/uhcireg.h>
+#include <dev/usb/uhcivar.h>
+
+#define PCI_UHCI_VENDORID_INTEL 0x8086
+#define PCI_UHCI_VENDORID_VIA 0x1106
+
+#define PCI_UHCI_DEVICEID_PIIX3 0x70208086
+static const char *uhci_device_piix3 = "Intel 82371SB (PIIX3) USB controller";
+
+#define PCI_UHCI_DEVICEID_PIIX4 0x71128086
+#define PCI_UHCI_DEVICEID_PIIX4E 0x71128086 /* no separate stepping */
+static const char *uhci_device_piix4 = "Intel 82371AB/EB (PIIX4) USB controller";
+
+#define PCI_UHCI_DEVICEID_ICH 0x24128086
+static const char *uhci_device_ich = "Intel 82801AA (ICH) USB controller";
+
+#define PCI_UHCI_DEVICEID_ICH0 0x24228086
+static const char *uhci_device_ich0 = "Intel 82801AB (ICH0) USB controller";
+
+#define PCI_UHCI_DEVICEID_ICH2_A 0x24428086
+static const char *uhci_device_ich2_a = "Intel 82801BA/BAM (ICH2) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH2_B 0x24448086
+static const char *uhci_device_ich2_b = "Intel 82801BA/BAM (ICH2) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH3_A 0x24828086
+static const char *uhci_device_ich3_a = "Intel 82801CA/CAM (ICH3) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH3_B 0x24848086
+static const char *uhci_device_ich3_b = "Intel 82801CA/CAM (ICH3) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH3_C 0x24878086
+static const char *uhci_device_ich3_c = "Intel 82801CA/CAM (ICH3) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH4_A 0x24c28086
+static const char *uhci_device_ich4_a = "Intel 82801DB (ICH4) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH4_B 0x24c48086
+static const char *uhci_device_ich4_b = "Intel 82801DB (ICH4) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH4_C 0x24c78086
+static const char *uhci_device_ich4_c = "Intel 82801DB (ICH4) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH5_A 0x24d28086
+static const char *uhci_device_ich5_a = "Intel 82801EB (ICH5) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH5_B 0x24d48086
+static const char *uhci_device_ich5_b = "Intel 82801EB (ICH5) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH5_C 0x24d78086
+static const char *uhci_device_ich5_c = "Intel 82801EB (ICH5) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH5_D 0x24de8086
+static const char *uhci_device_ich5_d = "Intel 82801EB (ICH5) USB controller USB-D";
+
+#define PCI_UHCI_DEVICEID_ICH6_A 0x26588086
+static const char *uhci_device_ich6_a = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH6_B 0x26598086
+static const char *uhci_device_ich6_b = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH6_C 0x265a8086
+static const char *uhci_device_ich6_c = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH6_D 0x265b8086
+static const char *uhci_device_ich6_d = "Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D";
+
+#define PCI_UHCI_DEVICEID_63XXESB_1 0x26888086
+static const char *uhci_device_esb_1 = "Intel 631XESB/632XESB/3100 USB controller USB-1";
+
+#define PCI_UHCI_DEVICEID_63XXESB_2 0x26898086
+static const char *uhci_device_esb_2 = "Intel 631XESB/632XESB/3100 USB controller USB-2";
+
+#define PCI_UHCI_DEVICEID_63XXESB_3 0x268a8086
+static const char *uhci_device_esb_3 = "Intel 631XESB/632XESB/3100 USB controller USB-3";
+
+#define PCI_UHCI_DEVICEID_63XXESB_4 0x268b8086
+static const char *uhci_device_esb_4 = "Intel 631XESB/632XESB/3100 USB controller USB-4";
+
+#define PCI_UHCI_DEVICEID_ICH8_A 0x28308086
+static const char *uhci_device_ich8_a = "Intel 82801H (ICH8) USB controller USB-A";
+
+#define PCI_UHCI_DEVICEID_ICH8_B 0x28318086
+static const char *uhci_device_ich8_b = "Intel 82801H (ICH8) USB controller USB-B";
+
+#define PCI_UHCI_DEVICEID_ICH8_C 0x28328086
+static const char *uhci_device_ich8_c = "Intel 82801H (ICH8) USB controller USB-C";
+
+#define PCI_UHCI_DEVICEID_ICH8_D 0x28348086
+static const char *uhci_device_ich8_d = "Intel 82801H (ICH8) USB controller USB-D";
+
+#define PCI_UHCI_DEVICEID_ICH8_E 0x28358086
+static const char *uhci_device_ich8_e = "Intel 82801H (ICH8) USB controller USB-E";
+
+#define PCI_UHCI_DEVICEID_ICH9_A 0x29348086
+#define PCI_UHCI_DEVICEID_ICH9_B 0x29358086
+#define PCI_UHCI_DEVICEID_ICH9_C 0x29368086
+#define PCI_UHCI_DEVICEID_ICH9_D 0x29378086
+#define PCI_UHCI_DEVICEID_ICH9_E 0x29388086
+#define PCI_UHCI_DEVICEID_ICH9_F 0x29398086
+static const char *uhci_device_ich9 = "Intel 82801I (ICH9) USB controller";
+
+#define PCI_UHCI_DEVICEID_440MX 0x719a8086
+static const char *uhci_device_440mx = "Intel 82443MX USB controller";
+
+#define PCI_UHCI_DEVICEID_460GX 0x76028086
+static const char *uhci_device_460gx = "Intel 82372FB/82468GX USB controller";
+
+#define PCI_UHCI_DEVICEID_VT83C572 0x30381106
+static const char *uhci_device_vt83c572 = "VIA 83C572 USB controller";
+
+static const char *uhci_device_generic = "UHCI (generic) USB controller";
+
+#define PCI_UHCI_BASE_REG 0x20
+
+
+static device_attach_t uhci_pci_attach;
+static device_detach_t uhci_pci_detach;
+static device_suspend_t uhci_pci_suspend;
+static device_resume_t uhci_pci_resume;
+
+static int
+uhci_pci_suspend(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return err;
+ uhci_power(PWR_SUSPEND, sc);
+
+ return 0;
+}
+
+static int
+uhci_pci_resume(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+
+ pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
+
+ uhci_power(PWR_RESUME, sc);
+ bus_generic_resume(self);
+
+ return 0;
+}
+
+static const char *
+uhci_pci_match(device_t self)
+{
+ u_int32_t device_id = pci_get_devid(self);
+
+ if (device_id == PCI_UHCI_DEVICEID_PIIX3) {
+ return (uhci_device_piix3);
+ } else if (device_id == PCI_UHCI_DEVICEID_PIIX4) {
+ return (uhci_device_piix4);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH) {
+ return (uhci_device_ich);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH0) {
+ return (uhci_device_ich0);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH2_A) {
+ return (uhci_device_ich2_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH2_B) {
+ return (uhci_device_ich2_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH3_A) {
+ return (uhci_device_ich3_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH3_B) {
+ return (uhci_device_ich3_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH3_C) {
+ return (uhci_device_ich3_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH4_A) {
+ return (uhci_device_ich4_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH4_B) {
+ return (uhci_device_ich4_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH4_C) {
+ return (uhci_device_ich4_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_A) {
+ return (uhci_device_ich5_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_B) {
+ return (uhci_device_ich5_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_C) {
+ return (uhci_device_ich5_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH5_D) {
+ return (uhci_device_ich5_d);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH6_A) {
+ return (uhci_device_ich6_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH6_B) {
+ return (uhci_device_ich6_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH6_C) {
+ return (uhci_device_ich6_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH6_D) {
+ return (uhci_device_ich6_d);
+ } else if (device_id == PCI_UHCI_DEVICEID_63XXESB_1) {
+ return (uhci_device_esb_1);
+ } else if (device_id == PCI_UHCI_DEVICEID_63XXESB_2) {
+ return (uhci_device_esb_2);
+ } else if (device_id == PCI_UHCI_DEVICEID_63XXESB_3) {
+ return (uhci_device_esb_3);
+ } else if (device_id == PCI_UHCI_DEVICEID_63XXESB_4) {
+ return (uhci_device_esb_4);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH8_A) {
+ return (uhci_device_ich8_a);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH8_B) {
+ return (uhci_device_ich8_b);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH8_C) {
+ return (uhci_device_ich8_c);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH8_D) {
+ return (uhci_device_ich8_d);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH8_E) {
+ return (uhci_device_ich8_e);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH9_A) {
+ return (uhci_device_ich9);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH9_B) {
+ return (uhci_device_ich9);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH9_C) {
+ return (uhci_device_ich9);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH9_D) {
+ return (uhci_device_ich9);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH9_E) {
+ return (uhci_device_ich9);
+ } else if (device_id == PCI_UHCI_DEVICEID_ICH9_F) {
+ return (uhci_device_ich9);
+ } else if (device_id == PCI_UHCI_DEVICEID_440MX) {
+ return (uhci_device_440mx);
+ } else if (device_id == PCI_UHCI_DEVICEID_460GX) {
+ return (uhci_device_460gx);
+ } else if (device_id == PCI_UHCI_DEVICEID_VT83C572) {
+ return (uhci_device_vt83c572);
+ } else {
+ if (pci_get_class(self) == PCIC_SERIALBUS
+ && pci_get_subclass(self) == PCIS_SERIALBUS_USB
+ && pci_get_progif(self) == PCI_INTERFACE_UHCI) {
+ return (uhci_device_generic);
+ }
+ }
+
+ return NULL; /* dunno... */
+}
+
+static int
+uhci_pci_probe(device_t self)
+{
+ const char *desc = uhci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return BUS_PROBE_DEFAULT;
+ } else {
+ return ENXIO;
+ }
+}
+
+static int
+uhci_pci_attach(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+ int rid;
+ int err;
+
+ pci_enable_busmaster(self);
+
+ rid = PCI_UHCI_BASE_REG;
+ sc->io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid,
+ RF_ACTIVE);
+ if (!sc->io_res) {
+ device_printf(self, "Could not map ports\n");
+ return ENXIO;
+ }
+ sc->iot = rman_get_bustag(sc->io_res);
+ sc->ioh = rman_get_bushandle(sc->io_res);
+
+ /* disable interrupts */
+ bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ uhci_pci_detach(self);
+ return ENXIO;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usb", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ uhci_pci_detach(self);
+ return ENOMEM;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ /* uhci_pci_match must never return NULL if uhci_pci_probe succeeded */
+ device_set_desc(sc->sc_bus.bdev, uhci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_UHCI_VENDORID_INTEL:
+ sprintf(sc->sc_vendor, "Intel");
+ break;
+ case PCI_UHCI_VENDORID_VIA:
+ sprintf(sc->sc_vendor, "VIA");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New UHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USBREV_MASK) {
+ case PCI_USBREV_PRE_1_0:
+ sc->sc_bus.usbrev = USBREV_PRE_1_0;
+ break;
+ case PCI_USBREV_1_0:
+ sc->sc_bus.usbrev = USBREV_1_0;
+ break;
+ default:
+ sc->sc_bus.usbrev = USBREV_UNKNOWN;
+ break;
+ }
+
+ /*
+ * Quirk for Parallels Desktop 4.0.
+ */
+ if (pci_get_devid(self) == PCI_UHCI_DEVICEID_ICH6_A)
+ sc->sc_bus.usbrev = USBREV_2_0;
+
+ err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
+ NULL, (driver_intr_t *) uhci_intr, sc, &sc->ih);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->ih = NULL;
+ uhci_pci_detach(self);
+ return ENXIO;
+ }
+ /*
+ * Set the PIRQD enable bit and switch off all the others. We don't
+ * want legacy support to interfere with us XXX Does this also mean
+ * that the BIOS won't touch the keyboard anymore if it is connected
+ * to the ports of the root hub?
+ */
+#ifdef USB_DEBUG
+ if (pci_read_config(self, PCI_LEGSUP, 2) != PCI_LEGSUP_USBPIRQDEN)
+ device_printf(self, "LegSup = 0x%04x\n",
+ pci_read_config(self, PCI_LEGSUP, 2));
+#endif
+ pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
+
+ /* Allocate a parent dma tag for DMA maps */
+ err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ NULL, NULL, &sc->sc_bus.parent_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate parent DMA tag (%d)\n",
+ err);
+ uhci_pci_detach(self);
+ return ENXIO;
+ }
+ /* Allocate a dma tag for transfer buffers */
+ err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
+ busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag);
+ if (err) {
+ device_printf(self, "Could not allocate transfer tag (%d)\n",
+ err);
+ uhci_pci_detach(self);
+ return ENXIO;
+ }
+
+ err = uhci_init(sc);
+ if (!err) {
+ sc->sc_flags |= UHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+
+ if (err) {
+ device_printf(self, "USB init failed\n");
+ uhci_pci_detach(self);
+ return EIO;
+ }
+ return 0; /* success */
+}
+
+int
+uhci_pci_detach(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+
+ if (sc->sc_flags & UHCI_SCFLG_DONEINIT) {
+ uhci_detach(sc, 0);
+ sc->sc_flags &= ~UHCI_SCFLG_DONEINIT;
+ }
+
+ if (sc->sc_bus.parent_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.parent_dmatag);
+ if (sc->sc_bus.buffer_dmatag != NULL)
+ bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag);
+
+ if (sc->irq_res && sc->ih) {
+ int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->ih = NULL;
+ }
+ if (sc->sc_bus.bdev) {
+ device_delete_child(self, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+ if (sc->irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res);
+ sc->irq_res = NULL;
+ }
+ if (sc->io_res) {
+ bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG,
+ sc->io_res);
+ sc->io_res = NULL;
+ sc->iot = 0;
+ sc->ioh = 0;
+ }
+ return 0;
+}
+
+
+static device_method_t uhci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uhci_pci_probe),
+ DEVMETHOD(device_attach, uhci_pci_attach),
+ DEVMETHOD(device_detach, uhci_pci_detach),
+ DEVMETHOD(device_suspend, uhci_pci_suspend),
+ DEVMETHOD(device_resume, uhci_pci_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t uhci_driver = {
+ "uhci",
+ uhci_methods,
+ sizeof(uhci_softc_t),
+};
+
+static devclass_t uhci_devclass;
+
+DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0);
+DRIVER_MODULE(uhci, cardbus, uhci_driver, uhci_devclass, 0, 0);
+MODULE_DEPEND(uhci, usb, 1, 1, 1);
diff --git a/sys/legacy/dev/usb/uhcireg.h b/sys/legacy/dev/usb/uhcireg.h
new file mode 100644
index 0000000..512dd2d
--- /dev/null
+++ b/sys/legacy/dev/usb/uhcireg.h
@@ -0,0 +1,193 @@
+/* $NetBSD: uhcireg.h,v 1.15 2002/02/11 11:41:30 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+#ifndef _DEV_PCI_UHCIREG_H_
+#define _DEV_PCI_UHCIREG_H_
+
+/*** PCI config registers ***/
+
+#define PCI_USBREV 0x60 /* USB protocol revision */
+#define PCI_USBREV_MASK 0xff
+#define PCI_USBREV_PRE_1_0 0x00
+#define PCI_USBREV_1_0 0x10
+#define PCI_USBREV_1_1 0x11
+
+#define PCI_LEGSUP 0xc0 /* Legacy Support register */
+#define PCI_LEGSUP_USBPIRQDEN 0x2000 /* USB PIRQ D Enable */
+
+#define PCI_CBIO 0x20 /* configuration base IO */
+
+#define PCI_INTERFACE_UHCI 0x00
+
+/*** UHCI registers ***/
+
+#define UHCI_CMD 0x00
+#define UHCI_CMD_RS 0x0001
+#define UHCI_CMD_HCRESET 0x0002
+#define UHCI_CMD_GRESET 0x0004
+#define UHCI_CMD_EGSM 0x0008
+#define UHCI_CMD_FGR 0x0010
+#define UHCI_CMD_SWDBG 0x0020
+#define UHCI_CMD_CF 0x0040
+#define UHCI_CMD_MAXP 0x0080
+
+#define UHCI_STS 0x02
+#define UHCI_STS_USBINT 0x0001
+#define UHCI_STS_USBEI 0x0002
+#define UHCI_STS_RD 0x0004
+#define UHCI_STS_HSE 0x0008
+#define UHCI_STS_HCPE 0x0010
+#define UHCI_STS_HCH 0x0020
+#define UHCI_STS_ALLINTRS 0x003f
+
+#define UHCI_INTR 0x04
+#define UHCI_INTR_TOCRCIE 0x0001
+#define UHCI_INTR_RIE 0x0002
+#define UHCI_INTR_IOCE 0x0004
+#define UHCI_INTR_SPIE 0x0008
+
+#define UHCI_FRNUM 0x06
+#define UHCI_FRNUM_MASK 0x03ff
+
+#define UHCI_FLBASEADDR 0x08
+
+#define UHCI_SOF 0x0c
+#define UHCI_SOF_MASK 0x7f
+
+#define UHCI_PORTSC1 0x010
+#define UHCI_PORTSC2 0x012
+#define UHCI_PORTSC_CCS 0x0001
+#define UHCI_PORTSC_CSC 0x0002
+#define UHCI_PORTSC_PE 0x0004
+#define UHCI_PORTSC_POEDC 0x0008
+#define UHCI_PORTSC_LS 0x0030
+#define UHCI_PORTSC_LS_SHIFT 4
+#define UHCI_PORTSC_RD 0x0040
+#define UHCI_PORTSC_LSDA 0x0100
+#define UHCI_PORTSC_PR 0x0200
+#define UHCI_PORTSC_OCI 0x0400
+#define UHCI_PORTSC_OCIC 0x0800
+#define UHCI_PORTSC_SUSP 0x1000
+
+#define URWMASK(x) \
+ ((x) & (UHCI_PORTSC_SUSP | UHCI_PORTSC_PR | UHCI_PORTSC_RD | UHCI_PORTSC_PE))
+
+#define UHCI_FRAMELIST_COUNT 1024
+#define UHCI_FRAMELIST_ALIGN 4096
+
+#define UHCI_TD_ALIGN 16
+#define UHCI_QH_ALIGN 16
+
+typedef u_int32_t uhci_physaddr_t;
+#define UHCI_PTR_T 0x00000001
+#define UHCI_PTR_TD 0x00000000
+#define UHCI_PTR_QH 0x00000002
+#define UHCI_PTR_VF 0x00000004
+
+/*
+ * Wait this long after a QH has been removed. This gives that HC a
+ * chance to stop looking at it before it's recycled.
+ */
+#define UHCI_QH_REMOVE_DELAY 5
+
+/*
+ * The Queue Heads and Transfer Descriptors are accessed
+ * by both the CPU and the USB controller which run
+ * concurrently. This means that they have to be accessed
+ * with great care. As long as the data structures are
+ * not linked into the controller's frame list they cannot
+ * be accessed by it and anything goes. As soon as a
+ * TD is accessible by the controller it "owns" the td_status
+ * field; it will not be written by the CPU. Similarly
+ * the controller "owns" the qh_elink field.
+ */
+
+typedef struct {
+ uhci_physaddr_t td_link;
+ u_int32_t td_status;
+#define UHCI_TD_GET_ACTLEN(s) (((s) + 1) & 0x3ff)
+#define UHCI_TD_ZERO_ACTLEN(t) ((t) | 0x3ff)
+#define UHCI_TD_BITSTUFF 0x00020000
+#define UHCI_TD_CRCTO 0x00040000
+#define UHCI_TD_NAK 0x00080000
+#define UHCI_TD_BABBLE 0x00100000
+#define UHCI_TD_DBUFFER 0x00200000
+#define UHCI_TD_STALLED 0x00400000
+#define UHCI_TD_ACTIVE 0x00800000
+#define UHCI_TD_IOC 0x01000000
+#define UHCI_TD_IOS 0x02000000
+#define UHCI_TD_LS 0x04000000
+#define UHCI_TD_GET_ERRCNT(s) (((s) >> 27) & 3)
+#define UHCI_TD_SET_ERRCNT(n) ((n) << 27)
+#define UHCI_TD_SPD 0x20000000
+ u_int32_t td_token;
+#define UHCI_TD_PID_IN 0x00000069
+#define UHCI_TD_PID_OUT 0x000000e1
+#define UHCI_TD_PID_SETUP 0x0000002d
+#define UHCI_TD_GET_PID(s) ((s) & 0xff)
+#define UHCI_TD_SET_DEVADDR(a) ((a) << 8)
+#define UHCI_TD_GET_DEVADDR(s) (((s) >> 8) & 0x7f)
+#define UHCI_TD_SET_ENDPT(e) (((e)&0xf) << 15)
+#define UHCI_TD_GET_ENDPT(s) (((s) >> 15) & 0xf)
+#define UHCI_TD_SET_DT(t) ((t) << 19)
+#define UHCI_TD_GET_DT(s) (((s) >> 19) & 1)
+#define UHCI_TD_SET_MAXLEN(l) (((l)-1) << 21)
+#define UHCI_TD_GET_MAXLEN(s) ((((s) >> 21) + 1) & 0x7ff)
+#define UHCI_TD_MAXLEN_MASK 0xffe00000
+ u_int32_t td_buffer;
+} uhci_td_t;
+
+#define UHCI_TD_ERROR (UHCI_TD_BITSTUFF|UHCI_TD_CRCTO|UHCI_TD_BABBLE|UHCI_TD_DBUFFER|UHCI_TD_STALLED)
+
+#define UHCI_TD_SETUP(len, endp, dev) (UHCI_TD_SET_MAXLEN(len) | \
+ UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_SETUP)
+#define UHCI_TD_OUT(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | \
+ UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | \
+ UHCI_TD_PID_OUT | UHCI_TD_SET_DT(dt))
+#define UHCI_TD_IN(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | \
+ UHCI_TD_SET_ENDPT(endp) | UHCI_TD_SET_DEVADDR(dev) | UHCI_TD_PID_IN | \
+ UHCI_TD_SET_DT(dt))
+
+typedef struct {
+ uhci_physaddr_t qh_hlink;
+ uhci_physaddr_t qh_elink;
+} uhci_qh_t;
+
+#endif /* _DEV_PCI_UHCIREG_H_ */
diff --git a/sys/legacy/dev/usb/uhcivar.h b/sys/legacy/dev/usb/uhcivar.h
new file mode 100644
index 0000000..0df23f1
--- /dev/null
+++ b/sys/legacy/dev/usb/uhcivar.h
@@ -0,0 +1,206 @@
+/* $NetBSD: uhcivar.h,v 1.33 2002/02/11 11:41:30 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+/*
+ * To avoid having 1024 TDs for each isochronous transfer we introduce
+ * a virtual frame list. Every UHCI_VFRAMELIST_COUNT entries in the real
+ * frame list points to a non-active TD. These, in turn, form the
+ * starts of the virtual frame list. This also has the advantage that it
+ * simplifies linking in/out of TDs/QHs in the schedule.
+ * Furthermore, initially each of the inactive TDs point to an inactive
+ * QH that forms the start of the interrupt traffic for that slot.
+ * Each of these QHs point to the same QH that is the start of control
+ * traffic. This QH points at another QH which is the start of the
+ * bulk traffic.
+ *
+ * UHCI_VFRAMELIST_COUNT should be a power of 2 and <= UHCI_FRAMELIST_COUNT.
+ */
+#define UHCI_VFRAMELIST_COUNT 128
+
+typedef struct uhci_soft_qh uhci_soft_qh_t;
+typedef struct uhci_soft_td uhci_soft_td_t;
+
+typedef union {
+ struct uhci_soft_qh *sqh;
+ struct uhci_soft_td *std;
+} uhci_soft_td_qh_t;
+
+/*
+ * An interrupt info struct contains the information needed to
+ * execute a requested routine when the controller generates an
+ * interrupt. Since we cannot know which transfer generated
+ * the interrupt all structs are linked together so they can be
+ * searched at interrupt time.
+ */
+typedef struct uhci_intr_info {
+ struct uhci_softc *sc;
+ usbd_xfer_handle xfer;
+ uhci_soft_td_t *stdstart;
+ uhci_soft_td_t *stdend;
+ LIST_ENTRY(uhci_intr_info) list;
+#ifdef DIAGNOSTIC
+ int isdone;
+#endif
+} uhci_intr_info_t;
+
+struct uhci_xfer {
+ struct usbd_xfer xfer;
+ uhci_intr_info_t iinfo;
+ struct usb_task abort_task;
+ int curframe;
+ u_int32_t uhci_xfer_flags;
+};
+
+#define UHCI_XFER_ABORTING 0x0001 /* xfer is aborting. */
+#define UHCI_XFER_ABORTWAIT 0x0002 /* abort completion is being awaited. */
+
+#define UXFER(xfer) ((struct uhci_xfer *)(xfer))
+
+/*
+ * Extra information that we need for a TD.
+ */
+struct uhci_soft_td {
+ uhci_td_t td; /* The real TD, must be first */
+ uhci_soft_td_qh_t link; /* soft version of the td_link field */
+ uhci_physaddr_t physaddr; /* TD's physical address. */
+ usb_dma_t aux_dma; /* Auxillary storage if needed. */
+ void *aux_data; /* Original aux data virtual address. */
+ int aux_len; /* Auxillary storage size. */
+};
+/*
+ * Make the size such that it is a multiple of UHCI_TD_ALIGN. This way
+ * we can pack a number of soft TD together and have the real TD well
+ * aligned.
+ * NOTE: Minimum size is 32 bytes.
+ */
+#define UHCI_STD_SIZE ((sizeof (struct uhci_soft_td) + UHCI_TD_ALIGN - 1) / UHCI_TD_ALIGN * UHCI_TD_ALIGN)
+#define UHCI_STD_CHUNK (PAGE_SIZE / UHCI_STD_SIZE)
+
+/*
+ * Extra information that we need for a QH.
+ */
+struct uhci_soft_qh {
+ uhci_qh_t qh; /* The real QH, must be first */
+ uhci_soft_qh_t *hlink; /* soft version of qh_hlink */
+ uhci_soft_td_t *elink; /* soft version of qh_elink */
+ uhci_physaddr_t physaddr; /* QH's physical address. */
+ int pos; /* Timeslot position */
+};
+/* See comment about UHCI_STD_SIZE. */
+#define UHCI_SQH_SIZE ((sizeof (struct uhci_soft_qh) + UHCI_QH_ALIGN - 1) / UHCI_QH_ALIGN * UHCI_QH_ALIGN)
+#define UHCI_SQH_CHUNK (PAGE_SIZE / UHCI_SQH_SIZE)
+
+/*
+ * Information about an entry in the virtual frame list.
+ */
+struct uhci_vframe {
+ uhci_soft_td_t *htd; /* pointer to dummy TD */
+ uhci_soft_td_t *etd; /* pointer to last TD */
+ uhci_soft_qh_t *hqh; /* pointer to dummy QH */
+ uhci_soft_qh_t *eqh; /* pointer to last QH */
+ u_int bandwidth; /* max bandwidth used by this frame */
+};
+
+#define UHCI_SCFLG_DONEINIT 0x0001 /* uhci_init() done */
+
+typedef struct uhci_softc {
+ struct usbd_bus sc_bus; /* base device */
+ int sc_flags;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t sc_size;
+ void *ih;
+
+ struct resource *io_res;
+ struct resource *irq_res;
+ uhci_physaddr_t *sc_pframes;
+ usb_dma_t sc_dma;
+ struct uhci_vframe sc_vframes[UHCI_VFRAMELIST_COUNT];
+
+ uhci_soft_qh_t *sc_lctl_start; /* dummy QH for low speed control */
+ uhci_soft_qh_t *sc_lctl_end; /* last control QH */
+ uhci_soft_qh_t *sc_hctl_start; /* dummy QH for high speed control */
+ uhci_soft_qh_t *sc_hctl_end; /* last control QH */
+ uhci_soft_qh_t *sc_bulk_start; /* dummy QH for bulk */
+ uhci_soft_qh_t *sc_bulk_end; /* last bulk transfer */
+ uhci_soft_qh_t *sc_last_qh; /* dummy QH at the end */
+ u_int32_t sc_loops; /* number of QHs that wants looping */
+
+ uhci_soft_td_t *sc_freetds; /* TD free list */
+ uhci_soft_qh_t *sc_freeqhs; /* QH free list */
+
+ STAILQ_HEAD(, usbd_xfer) sc_free_xfers; /* free xfers */
+
+ u_int8_t sc_addr; /* device address */
+ u_int8_t sc_conf; /* device configuration */
+
+ u_int8_t sc_saved_sof;
+ u_int16_t sc_saved_frnum;
+
+#ifdef USB_USE_SOFTINTR
+ char sc_softwake;
+#endif /* USB_USE_SOFTINTR */
+
+ char sc_isreset;
+ char sc_suspend;
+ char sc_dying;
+
+ LIST_HEAD(, uhci_intr_info) sc_intrhead;
+
+ /* Info for the root hub interrupt channel. */
+ int sc_ival; /* time between root hub intrs */
+ usbd_xfer_handle sc_intr_xfer; /* root hub interrupt transfer */
+ struct callout sc_poll_handle;
+
+ char sc_vendor[16]; /* vendor string for root hub */
+ int sc_id_vendor; /* vendor ID for root hub */
+
+#if defined(__NetBSD__)
+ void *sc_powerhook; /* cookie from power hook */
+ void *sc_shutdownhook; /* cookie from shutdown hook */
+#endif
+} uhci_softc_t;
+
+usbd_status uhci_init(uhci_softc_t *);
+int uhci_intr(void *);
+int uhci_detach(uhci_softc_t *, int);
+void uhci_shutdown(void *v);
+void uhci_power(int state, void *priv);
+
diff --git a/sys/legacy/dev/usb/uhid.c b/sys/legacy/dev/usb/uhid.c
new file mode 100644
index 0000000..d821c01
--- /dev/null
+++ b/sys/legacy/dev/usb/uhid.c
@@ -0,0 +1,755 @@
+/* $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 $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * 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
+ */
+
+/*
+ * XXX TODO: Convert this driver to use si_drv[12] rather than the
+ * devclass_get_softc junk
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/clist.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/signalvar.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/selinfo.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+#include <sys/ttycom.h>
+#include <sys/uio.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include "usbdevs.h"
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/hid.h>
+
+/* Replacement report descriptors for devices shipped with broken ones */
+#include <dev/usb/ugraphire_rdesc.h>
+#include <dev/usb/uxb360gp_rdesc.h>
+
+/* For hid blacklist quirk */
+#include <dev/usb/usb_quirks.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uhiddebug) printf x
+#define DPRINTFN(n,x) if (uhiddebug>(n)) printf x
+int uhiddebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid");
+SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RW,
+ &uhiddebug, 0, "uhid debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uhid_softc {
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface; /* interface */
+ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
+ int sc_ep_addr;
+
+ int sc_isize;
+ int sc_osize;
+ int sc_fsize;
+ u_int8_t sc_iid;
+ u_int8_t sc_oid;
+ u_int8_t sc_fid;
+
+ u_char *sc_ibuf;
+ u_char *sc_obuf;
+
+ void *sc_repdesc;
+ int sc_repdesc_size;
+
+ struct clist sc_q;
+ struct selinfo sc_rsel;
+ struct proc *sc_async; /* process that wants SIGIO */
+ u_char sc_state; /* driver state */
+#define UHID_OPEN 0x01 /* device is open */
+#define UHID_ASLP 0x02 /* waiting for device data */
+#define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */
+#define UHID_IMMED 0x08 /* return read data immediately */
+
+ int sc_refcnt;
+ u_char sc_dying;
+
+ struct cdev *dev;
+};
+
+#define UHIDUNIT(dev) (dev2unit(dev))
+#define UHID_CHUNK 128 /* chunk size for read */
+#define UHID_BSIZE 1020 /* buffer size */
+
+d_open_t uhidopen;
+d_close_t uhidclose;
+d_read_t uhidread;
+d_write_t uhidwrite;
+d_ioctl_t uhidioctl;
+d_poll_t uhidpoll;
+
+
+static struct cdevsw uhid_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = uhidopen,
+ .d_close = uhidclose,
+ .d_read = uhidread,
+ .d_write = uhidwrite,
+ .d_ioctl = uhidioctl,
+ .d_poll = uhidpoll,
+ .d_name = "uhid",
+};
+
+static void uhid_intr(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+
+static int uhid_do_read(struct uhid_softc *, struct uio *uio, int);
+static int uhid_do_write(struct uhid_softc *, struct uio *uio, int);
+static int uhid_do_ioctl(struct uhid_softc *, u_long, caddr_t, int,
+ struct thread *);
+
+MODULE_DEPEND(uhid, usb, 1, 1, 1);
+
+static device_probe_t uhid_match;
+static device_attach_t uhid_attach;
+static device_detach_t uhid_detach;
+
+static device_method_t uhid_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uhid_match),
+ DEVMETHOD(device_attach, uhid_attach),
+ DEVMETHOD(device_detach, uhid_detach),
+
+ { 0, 0 }
+};
+
+static driver_t uhid_driver = {
+ "uhid",
+ uhid_methods,
+ sizeof(struct uhid_softc)
+};
+
+static devclass_t uhid_devclass;
+
+DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0);
+
+static int
+uhid_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+
+ 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);
+#if 0
+ if (uaa->matchlvl)
+ return (uaa->matchlvl);
+#endif
+
+ return (UMATCH_IFACECLASS_GENERIC);
+}
+
+static int
+uhid_attach(device_t self)
+{
+ struct uhid_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int size;
+ void *desc;
+ const void *descptr;
+ usbd_status err;
+
+ sc->sc_dev = self;
+ sc->sc_udev = uaa->device;
+ sc->sc_iface = iface;
+ id = usbd_get_interface_descriptor(iface);
+
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (ed == NULL) {
+ printf("%s: could not read endpoint descriptor\n",
+ device_get_nameunit(sc->sc_dev));
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d "
+ "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
+ " bInterval=%d\n",
+ ed->bLength, ed->bDescriptorType,
+ ed->bEndpointAddress & UE_ADDR,
+ UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
+ ed->bmAttributes & UE_XFERTYPE,
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
+ (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ printf("%s: unexpected endpoint\n", device_get_nameunit(sc->sc_dev));
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ sc->sc_ep_addr = ed->bEndpointAddress;
+
+ descptr = NULL;
+ if (uaa->vendor == USB_VENDOR_WACOM) {
+ /* The report descriptor for the Wacom Graphire is broken. */
+ if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE) {
+ size = sizeof uhid_graphire_report_descr;
+ descptr = uhid_graphire_report_descr;
+ } else if (uaa->product == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) {
+ static uByte reportbuf[] = {2, 2, 2};
+
+ /*
+ * The Graphire3 needs 0x0202 to be written to
+ * feature report ID 2 before it'll start
+ * returning digitizer data.
+ */
+ usbd_set_report(uaa->iface, UHID_FEATURE_REPORT, 2,
+ &reportbuf, sizeof reportbuf);
+
+ size = sizeof uhid_graphire3_4x5_report_descr;
+ descptr = uhid_graphire3_4x5_report_descr;
+ }
+ } else if (id->bInterfaceClass == UICLASS_VENDOR &&
+ id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
+ id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD) {
+ static uByte reportbuf[] = {1, 3, 0};
+
+ /* The LEDs on the gamepad are blinking by default, turn off. */
+ usbd_set_report(uaa->iface, UHID_OUTPUT_REPORT, 0,
+ &reportbuf, sizeof reportbuf);
+
+ /* The Xbox 360 gamepad has no report descriptor. */
+ size = sizeof uhid_xb360gp_report_descr;
+ descptr = uhid_xb360gp_report_descr;
+ }
+
+ if (descptr) {
+ desc = malloc(size, M_USBDEV, M_NOWAIT);
+ if (desc == NULL)
+ err = USBD_NOMEM;
+ else {
+ err = USBD_NORMAL_COMPLETION;
+ memcpy(desc, descptr, size);
+ }
+ } else {
+ desc = NULL;
+ err = usbd_read_report_desc(uaa->iface, &desc, &size,M_USBDEV);
+ }
+
+ if (err) {
+ printf("%s: no report descriptor\n", device_get_nameunit(sc->sc_dev));
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ (void)usbd_set_idle(iface, 0, 0);
+
+ sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
+ sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid);
+ sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid);
+
+ sc->sc_repdesc = desc;
+ sc->sc_repdesc_size = size;
+ sc->dev = make_dev(&uhid_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "uhid%d", device_get_unit(self));
+ return 0;
+}
+
+static int
+uhid_detach(device_t self)
+{
+ struct uhid_softc *sc = device_get_softc(self);
+ int s;
+
+ DPRINTF(("uhid_detach: sc=%p\n", sc));
+ sc->sc_dying = 1;
+ if (sc->sc_intrpipe != NULL)
+ usbd_abort_pipe(sc->sc_intrpipe);
+
+ if (sc->sc_state & UHID_OPEN) {
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wake everyone */
+ wakeup(&sc->sc_q);
+ /* Wait for processes to go away. */
+ usb_detach_wait(sc->sc_dev);
+ }
+ splx(s);
+ }
+ destroy_dev(sc->dev);
+
+ if (sc->sc_repdesc)
+ free(sc->sc_repdesc, M_USBDEV);
+
+ return (0);
+}
+
+void
+uhid_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ struct uhid_softc *sc = addr;
+
+#ifdef USB_DEBUG
+ if (uhiddebug > 5) {
+ u_int32_t cc, i;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
+ DPRINTF(("uhid_intr: status=%d cc=%d\n", status, cc));
+ DPRINTF(("uhid_intr: data ="));
+ for (i = 0; i < cc; i++)
+ DPRINTF((" %02x", sc->sc_ibuf[i]));
+ DPRINTF(("\n"));
+ }
+#endif
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("uhid_intr: status=%d\n", status));
+ if (status == USBD_STALLED)
+ sc->sc_state |= UHID_NEEDCLEAR;
+ return;
+ }
+
+ (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
+
+ if (sc->sc_state & UHID_ASLP) {
+ sc->sc_state &= ~UHID_ASLP;
+ DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q));
+ wakeup(&sc->sc_q);
+ }
+ selwakeuppri(&sc->sc_rsel, PZERO);
+ if (sc->sc_async != NULL) {
+ DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async));
+ PROC_LOCK(sc->sc_async);
+ psignal(sc->sc_async, SIGIO);
+ PROC_UNLOCK(sc->sc_async);
+ }
+}
+
+int
+uhidopen(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct uhid_softc *sc;
+ usbd_status err;
+
+ sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+ if (sc == NULL)
+ return (ENXIO);
+
+ DPRINTF(("uhidopen: sc=%p\n", sc));
+
+ if (sc->sc_dying)
+ return (ENXIO);
+
+ if (sc->sc_state & UHID_OPEN)
+ return (EBUSY);
+ sc->sc_state |= UHID_OPEN;
+
+ clist_alloc_cblocks(&sc->sc_q, UHID_BSIZE, UHID_BSIZE);
+ sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK);
+
+ /* Set up interrupt pipe. */
+ err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
+ USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf,
+ sc->sc_isize, uhid_intr, USBD_DEFAULT_INTERVAL);
+ if (err) {
+ DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
+ "error=%d\n",err));
+ free(sc->sc_ibuf, M_USBDEV);
+ free(sc->sc_obuf, M_USBDEV);
+ sc->sc_ibuf = sc->sc_obuf = NULL;
+
+ sc->sc_state &= ~UHID_OPEN;
+ return (EIO);
+ }
+
+ sc->sc_state &= ~UHID_IMMED;
+
+ sc->sc_async = 0;
+
+ return (0);
+}
+
+int
+uhidclose(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct uhid_softc *sc;
+
+ sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+
+ DPRINTF(("uhidclose: sc=%p\n", sc));
+
+ /* Disable interrupts. */
+ usbd_abort_pipe(sc->sc_intrpipe);
+ usbd_close_pipe(sc->sc_intrpipe);
+ sc->sc_intrpipe = 0;
+
+ ndflush(&sc->sc_q, sc->sc_q.c_cc);
+ clist_free_cblocks(&sc->sc_q);
+
+ free(sc->sc_ibuf, M_USBDEV);
+ free(sc->sc_obuf, M_USBDEV);
+ sc->sc_ibuf = sc->sc_obuf = NULL;
+
+ sc->sc_state &= ~UHID_OPEN;
+
+ sc->sc_async = 0;
+
+ return (0);
+}
+
+int
+uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag)
+{
+ int s;
+ int error = 0;
+ size_t length;
+ u_char buffer[UHID_CHUNK];
+ usbd_status err;
+
+ DPRINTFN(1, ("uhidread\n"));
+ if (sc->sc_state & UHID_IMMED) {
+ DPRINTFN(1, ("uhidread immed\n"));
+
+ err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
+ sc->sc_iid, buffer, sc->sc_isize);
+ if (err)
+ return (EIO);
+ return (uiomove(buffer, sc->sc_isize, uio));
+ }
+
+ s = splusb();
+ while (sc->sc_q.c_cc == 0) {
+ if (flag & O_NONBLOCK) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ sc->sc_state |= UHID_ASLP;
+ DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q));
+ error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0);
+ DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
+ if (sc->sc_dying)
+ error = EIO;
+ if (error) {
+ sc->sc_state &= ~UHID_ASLP;
+ break;
+ }
+ if (sc->sc_state & UHID_NEEDCLEAR) {
+ DPRINTFN(-1,("uhidread: clearing stall\n"));
+ sc->sc_state &= ~UHID_NEEDCLEAR;
+ usbd_clear_endpoint_stall(sc->sc_intrpipe);
+ }
+ }
+ splx(s);
+
+ /* Transfer as many chunks as possible. */
+ while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
+ length = min(sc->sc_q.c_cc, uio->uio_resid);
+ if (length > sizeof(buffer))
+ length = sizeof(buffer);
+
+ /* Remove a small chunk from the input queue. */
+ (void) q_to_b(&sc->sc_q, buffer, length);
+ DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length));
+
+ /* Copy the data to the user process. */
+ if ((error = uiomove(buffer, length, uio)) != 0)
+ break;
+ }
+
+ return (error);
+}
+
+int
+uhidread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uhid_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+ sc->sc_refcnt++;
+ error = uhid_do_read(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ return (error);
+}
+
+int
+uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag)
+{
+ int error;
+ int size;
+ usbd_status err;
+
+ DPRINTFN(1, ("uhidwrite\n"));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ size = sc->sc_osize;
+ error = 0;
+ if (uio->uio_resid != size)
+ return (EINVAL);
+ error = uiomove(sc->sc_obuf, size, uio);
+ if (!error) {
+ if (sc->sc_oid)
+ err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
+ sc->sc_obuf[0], sc->sc_obuf+1, size-1);
+ else
+ err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
+ 0, sc->sc_obuf, size);
+ if (err)
+ error = EIO;
+ }
+
+ return (error);
+}
+
+int
+uhidwrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uhid_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+ sc->sc_refcnt++;
+ error = uhid_do_write(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ return (error);
+}
+
+int
+uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, int flag,
+ struct thread *p)
+{
+ struct usb_ctl_report_desc *rd;
+ struct usb_ctl_report *re;
+ int size, id;
+ usbd_status err;
+
+ DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ break;
+
+ case FIOASYNC:
+ if (*(int *)addr) {
+ if (sc->sc_async != NULL)
+ return (EBUSY);
+ sc->sc_async = p->td_proc;
+ DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", sc->sc_async));
+ } else
+ sc->sc_async = NULL;
+ break;
+
+ /* XXX this is not the most general solution. */
+ case TIOCSPGRP:
+ if (sc->sc_async == NULL)
+ return (EINVAL);
+ if (*(int *)addr != sc->sc_async->p_pgid)
+ return (EPERM);
+ break;
+
+ case USB_GET_REPORT_DESC:
+ rd = (struct usb_ctl_report_desc *)addr;
+ size = min(sc->sc_repdesc_size, sizeof rd->ucrd_data);
+ rd->ucrd_size = size;
+ memcpy(rd->ucrd_data, sc->sc_repdesc, size);
+ break;
+
+ case USB_SET_IMMED:
+ if (*(int *)addr) {
+ /* XXX should read into ibuf, but does it matter? */
+ err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
+ sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
+ if (err)
+ return (EOPNOTSUPP);
+
+ sc->sc_state |= UHID_IMMED;
+ } else
+ sc->sc_state &= ~UHID_IMMED;
+ break;
+
+ case USB_GET_REPORT:
+ re = (struct usb_ctl_report *)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);
+ }
+ err = usbd_get_report(sc->sc_iface, re->ucr_report, id, re->ucr_data,
+ size);
+ if (err)
+ return (EIO);
+ break;
+
+ case USB_SET_REPORT:
+ re = (struct usb_ctl_report *)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);
+ }
+ err = usbd_set_report(sc->sc_iface, re->ucr_report, id, re->ucr_data,
+ size);
+ if (err)
+ return (EIO);
+ break;
+
+ case USB_GET_REPORT_ID:
+ *(int *)addr = 0; /* XXX: we only support reportid 0? */
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+uhidioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *p)
+{
+ struct uhid_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+ sc->sc_refcnt++;
+ error = uhid_do_ioctl(sc, cmd, addr, flag, p);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ return (error);
+}
+
+int
+uhidpoll(struct cdev *dev, int events, struct thread *p)
+{
+ struct uhid_softc *sc;
+ int revents = 0;
+ int s;
+
+ sc = devclass_get_softc(uhid_devclass, UHIDUNIT(dev));
+ if (sc->sc_dying)
+ return (EIO);
+
+ s = splusb();
+ if (events & (POLLOUT | POLLWRNORM))
+ revents |= events & (POLLOUT | POLLWRNORM);
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sc->sc_q.c_cc > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(p, &sc->sc_rsel);
+ }
+
+ splx(s);
+ return (revents);
+}
diff --git a/sys/legacy/dev/usb/uhub.c b/sys/legacy/dev/usb/uhub.c
new file mode 100644
index 0000000..86fbba2
--- /dev/null
+++ b/sys/legacy/dev/usb/uhub.c
@@ -0,0 +1,703 @@
+/* $NetBSD: uhub.c,v 1.68 2004/06/29 06:30:05 mycroft 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+
+#define UHUB_INTR_INTERVAL 255 /* ms */
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uhubdebug) printf x
+#define DPRINTFN(n,x) if (uhubdebug > (n)) printf x
+#define DEVPRINTF(x) if (uhubdebug) device_printf x
+#define DEVPRINTFN(n, x)if (uhubdebug > (n)) device_printf x
+int uhubdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB uhub");
+SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW,
+ &uhubdebug, 0, "uhub debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#define DEVPRINTF(x)
+#define DEVPRINTFN(n,x)
+#endif
+
+struct uhub_softc {
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_hub; /* USB device */
+ usbd_pipe_handle sc_ipipe; /* interrupt pipe */
+ u_int8_t sc_status[32]; /* max 255 ports */
+ u_char sc_running;
+};
+#define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol)
+#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
+#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
+
+static usbd_status uhub_explore(usbd_device_handle hub);
+static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);
+
+/*
+ * We need two attachment points:
+ * hub to usb and hub to hub
+ * Every other driver only connects to hubs
+ */
+
+static device_probe_t uhub_match;
+static device_attach_t uhub_attach;
+static device_detach_t uhub_detach;
+static bus_child_location_str_t uhub_child_location_str;
+static bus_child_pnpinfo_str_t uhub_child_pnpinfo_str;
+
+static device_method_t uhub_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uhub_match),
+ DEVMETHOD(device_attach, uhub_attach),
+ DEVMETHOD(device_detach, uhub_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_str),
+ DEVMETHOD(bus_child_location_str, uhub_child_location_str),
+ /* XXX driver_added needs special care */
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+ { 0, 0 }
+};
+
+static driver_t uhub_driver = {
+ "uhub",
+ uhub_methods,
+ sizeof(struct uhub_softc)
+};
+
+static devclass_t uhub_devclass;
+
+/* Create the driver instance for the hub connected to usb case. */
+devclass_t uhubroot_devclass;
+
+static device_method_t uhubroot_methods[] = {
+ DEVMETHOD(device_probe, uhub_match),
+ DEVMETHOD(device_attach, uhub_attach),
+ DEVMETHOD(device_detach, uhub_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD(bus_child_location_str, uhub_child_location_str),
+ DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_str),
+ /* XXX driver_added needs special care */
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ {0,0}
+};
+
+static driver_t uhubroot_driver = {
+ "uhub",
+ uhubroot_methods,
+ sizeof(struct uhub_softc)
+};
+
+static int
+uhub_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
+
+ DPRINTFN(5,("uhub_match, dd=%p\n", dd));
+ /*
+ * The subclass for hubs seems to be 0 for some and 1 for others,
+ * so we just ignore the subclass.
+ */
+ if (uaa->iface == NULL && dd->bDeviceClass == UDCLASS_HUB)
+ return (UMATCH_DEVCLASS_DEVSUBCLASS);
+ return (UMATCH_NONE);
+}
+
+int
+uhub_attach(device_t self)
+{
+ struct uhub_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usbd_status err;
+ struct usbd_hub *hub = NULL;
+ usb_device_request_t req;
+ usb_hub_descriptor_t hubdesc;
+ int p, port, nports, nremov, pwrdly;
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+ struct usbd_tt *tts = NULL;
+
+ DPRINTFN(1,("uhub_attach\n"));
+ sc->sc_hub = dev;
+ sc->sc_dev = self;
+
+ if (dev->depth > 0 && UHUB_IS_HIGH_SPEED(sc)) {
+ device_printf(sc->sc_dev, "%s transaction translator%s\n",
+ UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple",
+ UHUB_IS_SINGLE_TT(sc) ? "" : "s");
+ }
+ err = usbd_set_config_index(dev, 0, 1);
+ if (err) {
+ DEVPRINTF((sc->sc_dev, "configuration failed, error=%s\n",
+ usbd_errstr(err)));
+ return (ENXIO);
+ }
+
+ if (dev->depth > USB_HUB_MAX_DEPTH) {
+ device_printf(sc->sc_dev, "hub depth (%d) exceeded, hub ignored\n",
+ USB_HUB_MAX_DEPTH);
+ return (ENXIO);
+ }
+
+ /* Get hub descriptor. */
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, (dev->address > 1 ? UDESC_HUB : 0), 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
+ DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
+ err = usbd_do_request(dev, &req, &hubdesc);
+ nports = hubdesc.bNbrPorts;
+ if (!err && nports > 7) {
+ USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
+ err = usbd_do_request(dev, &req, &hubdesc);
+ }
+ if (err) {
+ DEVPRINTF((sc->sc_dev, "getting hub descriptor failed: %s\n",
+ usbd_errstr(err)));
+ return (ENXIO);
+ }
+
+ for (nremov = 0, port = 1; port <= nports; port++)
+ if (!UHD_NOT_REMOV(&hubdesc, port))
+ nremov++;
+ device_printf(sc->sc_dev, "%d port%s with %d removable, %s powered\n",
+ nports, nports != 1 ? "s" : "", nremov,
+ dev->self_powered ? "self" : "bus");
+
+ if (nports == 0) {
+ device_printf(sc->sc_dev, "no ports, hub ignored\n");
+ goto bad;
+ }
+
+ hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
+ M_USBDEV, M_NOWAIT);
+ if (hub == NULL) {
+ return (ENXIO);
+ }
+ dev->hub = hub;
+ dev->hub->hubsoftc = sc;
+ hub->explore = uhub_explore;
+ hub->hubdesc = hubdesc;
+
+ DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, "
+ "parent->selfpowered=%d\n",
+ dev->self_powered, dev->powersrc->parent,
+ dev->powersrc->parent ?
+ dev->powersrc->parent->self_powered : 0));
+
+ if (!dev->self_powered && dev->powersrc->parent != NULL &&
+ !dev->powersrc->parent->self_powered) {
+ device_printf(sc->sc_dev, "bus powered hub connected to bus "
+ "powered hub, ignored\n");
+ goto bad;
+ }
+
+ /* Set up interrupt pipe. */
+ err = usbd_device2interface_handle(dev, 0, &iface);
+ if (err) {
+ device_printf(sc->sc_dev, "no interface handle\n");
+ goto bad;
+ }
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (ed == NULL) {
+ device_printf(sc->sc_dev, "no endpoint descriptor\n");
+ goto bad;
+ }
+ if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
+ device_printf(sc->sc_dev, "bad interrupt endpoint\n");
+ goto bad;
+ }
+
+ err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
+ USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status,
+ (nports + 1 + 7) / 8, uhub_intr, UHUB_INTR_INTERVAL);
+ if (err) {
+ device_printf(sc->sc_dev, "cannot open interrupt pipe\n");
+ goto bad;
+ }
+
+ /* Wait with power off for a while. */
+ usbd_delay_ms(dev, USB_POWER_DOWN_TIME);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, sc->sc_dev);
+
+ /*
+ * To have the best chance of success we do things in the exact same
+ * order as Windoze98. This should not be necessary, but some
+ * devices do not follow the USB specs to the letter.
+ *
+ * These are the events on the bus when a hub is attached:
+ * Get device and config descriptors (see attach code)
+ * Get hub descriptor (see above)
+ * For all ports
+ * turn on power
+ * wait for power to become stable
+ * (all below happens in explore code)
+ * For all ports
+ * clear C_PORT_CONNECTION
+ * For all ports
+ * get port status
+ * if device connected
+ * wait 100 ms
+ * turn on reset
+ * wait
+ * clear C_PORT_RESET
+ * get port status
+ * proceed with device attachment
+ */
+
+ if (UHUB_IS_HIGH_SPEED(sc)) {
+ tts = malloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) *
+ sizeof (struct usbd_tt), M_USBDEV, M_NOWAIT);
+ if (!tts)
+ goto bad;
+ }
+
+ /* Set up data structures */
+ for (p = 0; p < nports; p++) {
+ struct usbd_port *up = &hub->ports[p];
+ up->device = NULL;
+ up->parent = dev;
+ up->portno = p+1;
+ if (dev->self_powered)
+ /* Self powered hub, give ports maximum current. */
+ up->power = USB_MAX_POWER;
+ else
+ up->power = USB_MIN_POWER;
+ up->restartcnt = 0;
+ if (UHUB_IS_HIGH_SPEED(sc)) {
+ up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
+ up->tt->hub = hub;
+ } else {
+ up->tt = NULL;
+ }
+ }
+
+ /* XXX should check for none, individual, or ganged power? */
+
+ pwrdly = dev->hub->hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR
+ + USB_EXTRA_POWER_UP_TIME;
+ for (port = 1; port <= nports; port++) {
+ /* Turn the power on. */
+ err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
+ if (err)
+ device_printf(sc->sc_dev,
+ "port %d power on failed, %s\n", port,
+ usbd_errstr(err));
+ DPRINTF(("usb_init_port: turn on port %d power\n", port));
+ /* Wait for stable power. */
+ usbd_delay_ms(dev, pwrdly);
+ }
+
+ /* The usual exploration will finish the setup. */
+
+ sc->sc_running = 1;
+ return (0);
+ bad:
+ if (hub)
+ free(hub, M_USBDEV);
+ dev->hub = NULL;
+ return (ENXIO);
+}
+
+usbd_status
+uhub_explore(usbd_device_handle dev)
+{
+ usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
+ struct uhub_softc *sc = dev->hub->hubsoftc;
+ struct usbd_port *up;
+ usbd_status err;
+ int speed;
+ int port;
+ int change, status;
+
+ DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address));
+
+ if (!sc->sc_running)
+ return (USBD_NOT_STARTED);
+
+ /* Ignore hubs that are too deep. */
+ if (dev->depth > USB_HUB_MAX_DEPTH)
+ return (USBD_TOO_DEEP);
+
+ for(port = 1; port <= hd->bNbrPorts; port++) {
+ up = &dev->hub->ports[port-1];
+ err = usbd_get_port_status(dev, port, &up->status);
+ if (err) {
+ DPRINTF(("uhub_explore: get port status failed, "
+ "error=%s\n", usbd_errstr(err)));
+ continue;
+ }
+ status = UGETW(up->status.wPortStatus);
+ change = UGETW(up->status.wPortChange);
+ DEVPRINTFN(3,(sc->sc_dev,
+ "uhub_explore: port %d status 0x%04x 0x%04x\n", port,
+ status, change));
+ if (change & UPS_C_PORT_ENABLED) {
+ DPRINTF(("uhub_explore: C_PORT_ENABLED 0x%x\n", change));
+ usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
+ if (change & UPS_C_CONNECT_STATUS) {
+ /* Ignore the port error if the device
+ vanished. */
+ } else if (status & UPS_PORT_ENABLED) {
+ device_printf(sc->sc_dev,
+ "illegal enable change, port %d\n", port);
+ } else {
+ /* Port error condition. */
+ if (up->restartcnt) /* no message first time */
+ device_printf(sc->sc_dev,
+ "port error, restarting port %d\n",
+ port);
+
+ if (up->restartcnt++ < USBD_RESTART_MAX)
+ goto disco;
+ else
+ device_printf(sc->sc_dev,
+ "port error, giving up port %d\n",
+ port);
+ }
+ }
+ if (!(change & UPS_C_CONNECT_STATUS)) {
+ DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_"
+ "STATUS\n", port));
+ /* No status change, just do recursive explore. */
+ if (up->device != NULL && up->device->hub != NULL)
+ up->device->hub->explore(up->device);
+#if 0 && defined(DIAGNOSTIC)
+ if (up->device == NULL &&
+ (status & UPS_CURRENT_CONNECT_STATUS))
+ deivce_printf(sc->sc_dev,
+ "connected, no device\n");
+#endif
+ continue;
+ }
+
+ /* We have a connect status change, handle it. */
+
+ DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
+ dev->address, port));
+ usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
+ /*usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);*/
+ /*
+ * If there is already a device on the port the change status
+ * must mean that is has disconnected. Looking at the
+ * current connect status is not enough to figure this out
+ * since a new unit may have been connected before we handle
+ * the disconnect.
+ */
+ disco:
+ if (up->device != NULL) {
+ /* Disconnected */
+ DPRINTF(("uhub_explore: device addr=%d disappeared "
+ "on port %d\n", up->device->address, port));
+ usb_disconnect_port(up, sc->sc_dev);
+ usbd_clear_port_feature(dev, port,
+ UHF_C_PORT_CONNECTION);
+ }
+ if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
+ /* Nothing connected, just ignore it. */
+ DPRINTFN(3,("uhub_explore: port=%d !CURRENT_CONNECT"
+ "_STATUS\n", port));
+ continue;
+ }
+
+ /* Connected */
+
+ if (!(status & UPS_PORT_POWER))
+ device_printf(sc->sc_dev,
+ "strange, connected port %d has no power\n", port);
+
+ /* Wait for maximum device power up time. */
+ usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY);
+
+ /* Reset port, which implies enabling it. */
+ if (usbd_reset_port(dev, port, &up->status)) {
+ device_printf(sc->sc_dev, "port %d reset failed\n",
+ port);
+ continue;
+ }
+ /* Get port status again, it might have changed during reset */
+ err = usbd_get_port_status(dev, port, &up->status);
+ if (err) {
+ DPRINTF(("uhub_explore: get port status failed, "
+ "error=%s\n", usbd_errstr(err)));
+ continue;
+ }
+ status = UGETW(up->status.wPortStatus);
+ change = UGETW(up->status.wPortChange);
+ if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
+ /* Nothing connected, just ignore it. */
+#ifdef DIAGNOSTIC
+ device_printf(sc->sc_dev,
+ "port %d, device disappeared after reset\n", port);
+#endif
+ continue;
+ }
+
+#if 0
+ if (UHUB_IS_HIGH_SPEED(sc) && !(status & UPS_HIGH_SPEED)) {
+ device_printf(sc->sc_dev,
+ "port %d, transaction translation not implemented,"
+ " low/full speed device ignored\n", port);
+ continue;
+ }
+#endif
+
+ /* Figure out device speed */
+ if (status & UPS_HIGH_SPEED)
+ speed = USB_SPEED_HIGH;
+ else if (status & UPS_LOW_SPEED)
+ speed = USB_SPEED_LOW;
+ else
+ speed = USB_SPEED_FULL;
+ /* Get device info and set its address. */
+ err = usbd_new_device(sc->sc_dev, dev->bus,
+ dev->depth + 1, speed, port, up);
+ /* XXX retry a few times? */
+ if (err) {
+ DPRINTFN(-1,("uhub_explore: usb_new_device failed, "
+ "error=%s\n", usbd_errstr(err)));
+ /* Avoid addressing problems by disabling. */
+ /* usbd_reset_port(dev, port, &up->status); */
+
+ /*
+ * The unit refused to accept a new address, or had
+ * some other serious problem. Since we cannot leave
+ * at 0 we have to disable the port instead.
+ */
+ device_printf(sc->sc_dev,
+ "device problem (%s), disabling port %d\n",
+ usbd_errstr(err), port);
+ usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE);
+ } else {
+ /* The port set up succeeded, reset error count. */
+ up->restartcnt = 0;
+
+ if (up->device->hub)
+ up->device->hub->explore(up->device);
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/*
+ * Called from process context when the hub is gone.
+ * Detach all devices on active ports.
+ */
+static int
+uhub_detach(device_t self)
+{
+ struct uhub_softc *sc = device_get_softc(self);
+ struct usbd_hub *hub = sc->sc_hub->hub;
+ struct usbd_port *rup;
+ int port, nports;
+
+ DPRINTF(("uhub_detach: sc=%port\n", sc));
+ if (hub == NULL) /* Must be partially working */
+ return (0);
+
+ usbd_abort_pipe(sc->sc_ipipe);
+ usbd_close_pipe(sc->sc_ipipe);
+
+ nports = hub->hubdesc.bNbrPorts;
+ for(port = 0; port < nports; port++) {
+ rup = &hub->ports[port];
+ if (rup->device)
+ usb_disconnect_port(rup, self);
+ }
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub, sc->sc_dev);
+
+ if (hub->ports[0].tt)
+ free(hub->ports[0].tt, M_USBDEV);
+ free(hub, M_USBDEV);
+ sc->sc_hub->hub = NULL;
+
+ return (0);
+}
+
+int
+uhub_child_location_str(device_t cbdev, device_t child, char *buf,
+ size_t buflen)
+{
+ struct uhub_softc *sc = device_get_softc(cbdev);
+ usbd_device_handle devhub = sc->sc_hub;
+ usbd_device_handle dev;
+ int nports;
+ int port;
+ int i;
+
+ mtx_lock(&Giant);
+ nports = devhub->hub->hubdesc.bNbrPorts;
+ for (port = 0; port < nports; port++) {
+ dev = devhub->hub->ports[port].device;
+ if (dev && dev->subdevs) {
+ for (i = 0; dev->subdevs[i]; i++) {
+ if (dev->subdevs[i] == child) {
+ if (dev->ifacenums == NULL) {
+ snprintf(buf, buflen,
+ "port=%i", port);
+ } else {
+ snprintf(buf, buflen,
+ "port=%i interface=%i",
+ port, dev->ifacenums[i]);
+ }
+ goto found_dev;
+ }
+ }
+ }
+ }
+ DPRINTFN(0,("uhub_child_location_str: device not on hub\n"));
+ buf[0] = '\0';
+found_dev:
+ mtx_unlock(&Giant);
+ return (0);
+}
+
+int
+uhub_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
+ size_t buflen)
+{
+ struct uhub_softc *sc = device_get_softc(cbdev);
+ usbd_device_handle devhub = sc->sc_hub;
+ usbd_device_handle dev;
+ struct usbd_interface *iface;
+ char serial[128];
+ int nports;
+ int port;
+ int i;
+
+ mtx_lock(&Giant);
+ nports = devhub->hub->hubdesc.bNbrPorts;
+ for (port = 0; port < nports; port++) {
+ dev = devhub->hub->ports[port].device;
+ if (dev && dev->subdevs) {
+ for (i = 0; dev->subdevs[i]; i++) {
+ if (dev->subdevs[i] == child) {
+ goto found_dev;
+ }
+ }
+ }
+ }
+ DPRINTFN(0,("uhub_child_pnpinfo_str: device not on hub\n"));
+ buf[0] = '\0';
+ mtx_unlock(&Giant);
+ return (0);
+
+found_dev:
+ /* XXX can sleep */
+ (void)usbd_get_string(dev, dev->ddesc.iSerialNumber, serial,
+ sizeof(serial));
+ if (dev->ifacenums == NULL) {
+ snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
+ "devclass=0x%02x devsubclass=0x%02x "
+ "release=0x%04x sernum=\"%s\"",
+ UGETW(dev->ddesc.idVendor), UGETW(dev->ddesc.idProduct),
+ dev->ddesc.bDeviceClass, dev->ddesc.bDeviceSubClass,
+ UGETW(dev->ddesc.bcdDevice), serial);
+ } else {
+ iface = &dev->ifaces[dev->ifacenums[i]];
+ snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
+ "devclass=0x%02x devsubclass=0x%02x "
+ "release=0x%04x sernum=\"%s\" "
+ "intclass=0x%02x intsubclass=0x%02x",
+ UGETW(dev->ddesc.idVendor), UGETW(dev->ddesc.idProduct),
+ dev->ddesc.bDeviceClass, dev->ddesc.bDeviceSubClass,
+ UGETW(dev->ddesc.bcdDevice), serial,
+ iface->idesc->bInterfaceClass,
+ iface->idesc->bInterfaceSubClass);
+ }
+ mtx_unlock(&Giant);
+ return (0);
+}
+
+/*
+ * Hub interrupt.
+ * This an indication that some port has changed status.
+ * Notify the bus event handler thread that we need
+ * to be explored again.
+ */
+void
+uhub_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ struct uhub_softc *sc = addr;
+
+ DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_ipipe);
+ else if (status == USBD_NORMAL_COMPLETION)
+ usb_needs_explore(sc->sc_hub);
+}
+
+MODULE_DEPEND(uhub, usb, 1, 1, 1);
+DRIVER_MODULE(uhub, usb, uhubroot_driver, uhubroot_devclass, 0, 0);
+DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/uipaq.c b/sys/legacy/dev/usb/uipaq.c
new file mode 100644
index 0000000..a231a67
--- /dev/null
+++ b/sys/legacy/dev/usb/uipaq.c
@@ -0,0 +1,822 @@
+/* $NetBSD: uipaq.c,v 1.4 2006/11/16 01:33:27 christos Exp $ */
+/* $OpenBSD: uipaq.c,v 1.1 2005/06/17 23:50:33 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2000-2005 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.
+ */
+
+/*
+ * iPAQ driver
+ *
+ * 19 July 2003: Incorporated changes suggested by Sam Lawrance from
+ * the uppc module
+ *
+ *
+ * Contact isis@cs.umd.edu if you have any questions/comments about this driver
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/module.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbcdc.h> /*UCDC_* stuff */
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef UIPAQ_DEBUG
+#define DPRINTF(x) if (uipaqdebug) printf x
+#define DPRINTFN(n,x) if (uipaqdebug>(n)) printf x
+int uipaqdebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UIPAQ_CONFIG_NO 1
+#define UIPAQ_IFACE_INDEX 0
+
+#define UIPAQIBUFSIZE 1024
+#define UIPAQOBUFSIZE 1024
+
+struct uipaq_softc {
+ struct ucom_softc sc_ucom;
+ u_int16_t sc_lcr; /* state for DTR/RTS */
+ u_int16_t sc_flags;
+
+};
+
+/* Callback routines */
+static void uipaq_set(void *, int, int, int);
+
+/* Support routines. */
+/* based on uppc module by Sam Lawrance */
+static void uipaq_dtr(struct uipaq_softc *sc, int onoff);
+static void uipaq_rts(struct uipaq_softc *sc, int onoff);
+static void uipaq_break(struct uipaq_softc* sc, int onoff);
+
+int uipaq_detach(device_t self);
+
+struct ucom_callback uipaq_callback = {
+ .ucom_set = uipaq_set
+};
+
+struct uipaq_type {
+ struct usb_devno uv_dev;
+ u_int16_t uv_flags;
+};
+
+/*
+ * Much of this list is generated from lists of other drivers that support
+ * the same hardware. Numeric values are used where no usbdevs entries
+ * exist.
+ */
+static const struct uipaq_type uipaq_devs[] = {
+ {{ 0x0104, 0x00be }, 0}, /* Socket USB Sync */
+ {{ 0x04ad, 0x0301 }, 0}, /* USB Sync 0301 */
+ {{ 0x04ad, 0x0302 }, 0}, /* USB Sync 0302 */
+ {{ 0x04ad, 0x0303 }, 0}, /* USB Sync 0303 */
+ {{ 0x04ad, 0x0306 }, 0}, /* GPS Pocket PC USB Sync */
+ {{ 0x0536, 0x01a0 }, 0}, /* HHP PDT */
+ {{ 0x067e, 0x1001 }, 0}, /* Intermec Mobile Computer */
+ {{ 0x094b, 0x0001 }, 0}, /* Linkup Systems USB Sync */
+ {{ 0x0960, 0x0065 }, 0}, /* BCOM USB Sync 0065 */
+ {{ 0x0960, 0x0066 }, 0}, /* BCOM USB Sync 0066 */
+ {{ 0x0960, 0x0067 }, 0}, /* BCOM USB Sync 0067 */
+ {{ 0x0961, 0x0010 }, 0}, /* Portatec USB Sync */
+ {{ 0x099e, 0x0052 }, 0}, /* Trimble GeoExplorer */
+ {{ 0x099e, 0x4000 }, 0}, /* TDS Data Collector */
+ {{ 0x0c44, 0x03a2 }, 0}, /* Motorola iDEN Smartphone */
+ {{ 0x0c8e, 0x6000 }, 0}, /* Cesscom Luxian Series */
+ {{ 0x0cad, 0x9001 }, 0}, /* Motorola PowerPad Pocket PCDevice */
+ {{ 0x0f4e, 0x0200 }, 0}, /* Freedom Scientific USB Sync */
+ {{ 0x0f98, 0x0201 }, 0}, /* Cyberbank USB Sync */
+ {{ 0x0fb8, 0x3001 }, 0}, /* Wistron USB Sync */
+ {{ 0x0fb8, 0x3002 }, 0}, /* Wistron USB Sync */
+ {{ 0x0fb8, 0x3003 }, 0}, /* Wistron USB Sync */
+ {{ 0x0fb8, 0x4001 }, 0}, /* Wistron USB Sync */
+ {{ 0x1066, 0x00ce }, 0}, /* E-TEN USB Sync */
+ {{ 0x1066, 0x0300 }, 0}, /* E-TEN P3XX Pocket PC */
+ {{ 0x1066, 0x0500 }, 0}, /* E-TEN P5XX Pocket PC */
+ {{ 0x1066, 0x0600 }, 0}, /* E-TEN P6XX Pocket PC */
+ {{ 0x1066, 0x0700 }, 0}, /* E-TEN P7XX Pocket PC */
+ {{ 0x1114, 0x0001 }, 0}, /* Psion Teklogix Sync 753x */
+ {{ 0x1114, 0x0004 }, 0}, /* Psion Teklogix Sync netBookPro */
+ {{ 0x1114, 0x0006 }, 0}, /* Psion Teklogix Sync 7525 */
+ {{ 0x1182, 0x1388 }, 0}, /* VES USB Sync */
+ {{ 0x11d9, 0x1002 }, 0}, /* Rugged Pocket PC 2003 */
+ {{ 0x11d9, 0x1003 }, 0}, /* Rugged Pocket PC 2003 */
+ {{ 0x1231, 0xce01 }, 0}, /* USB Sync 03 */
+ {{ 0x1231, 0xce02 }, 0}, /* USB Sync 03 */
+ {{ 0x3340, 0x011c }, 0}, /* Mio DigiWalker PPC StrongARM */
+ {{ 0x3340, 0x0326 }, 0}, /* Mio DigiWalker 338 */
+ {{ 0x3340, 0x0426 }, 0}, /* Mio DigiWalker 338 */
+ {{ 0x3340, 0x043a }, 0}, /* Mio DigiWalker USB Sync */
+ {{ 0x3340, 0x051c }, 0}, /* MiTAC USB Sync 528 */
+ {{ 0x3340, 0x053a }, 0}, /* Mio DigiWalker SmartPhone USB Sync */
+ {{ 0x3340, 0x071c }, 0}, /* MiTAC USB Sync */
+ {{ 0x3340, 0x0b1c }, 0}, /* Generic PPC StrongARM */
+ {{ 0x3340, 0x0e3a }, 0}, /* Generic PPC USB Sync */
+ {{ 0x3340, 0x0f1c }, 0}, /* Itautec USB Sync */
+ {{ 0x3340, 0x0f3a }, 0}, /* Generic SmartPhone USB Sync */
+ {{ 0x3340, 0x1326 }, 0}, /* Itautec USB Sync */
+ {{ 0x3340, 0x191c }, 0}, /* YAKUMO USB Sync */
+ {{ 0x3340, 0x2326 }, 0}, /* Vobis USB Sync */
+ {{ 0x3340, 0x3326 }, 0}, /* MEDION Winodws Moble USB Sync */
+ {{ 0x3708, 0x20ce }, 0}, /* Legend USB Sync */
+ {{ 0x3708, 0x21ce }, 0}, /* Lenovo USB Sync */
+ {{ 0x4113, 0x0210 }, 0}, /* Mobile Media Technology USB Sync */
+ {{ 0x4113, 0x0211 }, 0}, /* Mobile Media Technology USB Sync */
+ {{ 0x4113, 0x0400 }, 0}, /* Mobile Media Technology USB Sync */
+ {{ 0x4113, 0x0410 }, 0}, /* Mobile Media Technology USB Sync */
+ {{ 0x4505, 0x0010 }, 0}, /* Smartphone */
+ {{ 0x5e04, 0xce00 }, 0}, /* SAGEM Wireless Assistant */
+ {{ USB_VENDOR_ACER, 0x1631 }, 0}, /* c10 Series */
+ {{ USB_VENDOR_ACER, 0x1632 }, 0}, /* c20 Series */
+ {{ USB_VENDOR_ACER, 0x16e1 }, 0}, /* Acer n10 Handheld USB Sync */
+ {{ USB_VENDOR_ACER, 0x16e2 }, 0}, /* Acer n20 Handheld USB Sync */
+ {{ USB_VENDOR_ACER, 0x16e3 }, 0}, /* Acer n30 Handheld USB Sync */
+ {{ USB_VENDOR_ASUS, 0x4200 }, 0}, /* ASUS USB Sync */
+ {{ USB_VENDOR_ASUS, 0x4201 }, 0}, /* ASUS USB Sync */
+ {{ USB_VENDOR_ASUS, 0x4202 }, 0}, /* ASUS USB Sync */
+ {{ USB_VENDOR_ASUS, 0x9200 }, 0}, /* ASUS USB Sync */
+ {{ USB_VENDOR_ASUS, 0x9202 }, 0}, /* ASUS USB Sync */
+ {{ USB_VENDOR_ASUS, USB_PRODUCT_ASUS_P535 }, 0},
+ {{ USB_VENDOR_CASIO, 0x2001 }, 0}, /* CASIO USB Sync 2001 */
+ {{ USB_VENDOR_CASIO, 0x2003 }, 0}, /* CASIO USB Sync 2003 */
+ {{ USB_VENDOR_CASIO, USB_PRODUCT_CASIO_BE300 } , 0},
+ {{ USB_VENDOR_COMPAL, 0x0531 }, 0}, /* MyGuide 7000 XL USB Sync */
+ {{ USB_VENDOR_COMPAQ, 0x0032 }, 0}, /* Compaq iPAQ USB Sync */
+ {{ USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQPOCKETPC } , 0},
+ {{ USB_VENDOR_DELL, 0x4001 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4002 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4003 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4004 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4005 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4006 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4007 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4008 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_DELL, 0x4009 }, 0}, /* Dell Axim USB Sync */
+ {{ USB_VENDOR_FSC, 0x1001 }, 0}, /* Fujitsu Siemens Computers USB Sync */
+ {{ USB_VENDOR_FUJITSU, 0x1058 }, 0}, /* FUJITSU USB Sync */
+ {{ USB_VENDOR_FUJITSU, 0x1079 }, 0}, /* FUJITSU USB Sync */
+ {{ USB_VENDOR_GIGASET, 0x0601 }, 0}, /* Askey USB Sync */
+ {{ USB_VENDOR_HITACHI, 0x0014 }, 0}, /* Hitachi USB Sync */
+ {{ USB_VENDOR_HP, 0x1216 }, 0}, /* HP USB Sync 1612 */
+ {{ USB_VENDOR_HP, 0x2016 }, 0}, /* HP USB Sync 1620 */
+ {{ USB_VENDOR_HP, 0x2116 }, 0}, /* HP USB Sync 1621 */
+ {{ USB_VENDOR_HP, 0x2216 }, 0}, /* HP USB Sync 1622 */
+ {{ USB_VENDOR_HP, 0x3016 }, 0}, /* HP USB Sync 1630 */
+ {{ USB_VENDOR_HP, 0x3116 }, 0}, /* HP USB Sync 1631 */
+ {{ USB_VENDOR_HP, 0x3216 }, 0}, /* HP USB Sync 1632 */
+ {{ USB_VENDOR_HP, 0x4016 }, 0}, /* HP USB Sync 1640 */
+ {{ USB_VENDOR_HP, 0x4116 }, 0}, /* HP USB Sync 1641 */
+ {{ USB_VENDOR_HP, 0x4216 }, 0}, /* HP USB Sync 1642 */
+ {{ USB_VENDOR_HP, 0x5016 }, 0}, /* HP USB Sync 1650 */
+ {{ USB_VENDOR_HP, 0x5116 }, 0}, /* HP USB Sync 1651 */
+ {{ USB_VENDOR_HP, 0x5216 }, 0}, /* HP USB Sync 1652 */
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_2215 }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_568J }, 0},
+ {{ USB_VENDOR_HTC, 0x00cf }, 0}, /* HTC USB Modem */
+ {{ USB_VENDOR_HTC, 0x0a01 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a02 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a03 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a04 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a05 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a06 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a07 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a08 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a09 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a0a }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a0b }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a0c }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a0d }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a0e }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a0f }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a10 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a11 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a12 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a13 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a14 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a15 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a16 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a17 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a18 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a19 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a1a }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a1b }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a1c }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a1d }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a1e }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a1f }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a20 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a21 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a22 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a23 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a24 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a25 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a26 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a27 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a28 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a29 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a2a }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a2b }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a2c }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a2d }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a2e }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a2f }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a30 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a31 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a32 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a33 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a34 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a35 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a36 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a37 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a38 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a39 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a3a }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a3b }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a3c }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a3d }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a3e }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a3f }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a40 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a41 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a42 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a43 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a44 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a45 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a46 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a47 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a48 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a49 }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a4a }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a4b }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a4c }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a4d }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a4e }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a4f }, 0}, /* PocketPC USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a50 }, 0}, /* HTC SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a52 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a53 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a54 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a55 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a56 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a57 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a58 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a59 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a5a }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a5b }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a5c }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a5d }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a5e }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a5f }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a60 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a61 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a62 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a63 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a64 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a65 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a66 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a67 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a68 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a69 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a6a }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a6b }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a6c }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a6d }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a6e }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a6f }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a70 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a71 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a72 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a73 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a74 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a75 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a76 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a77 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a78 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a79 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a7a }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a7b }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a7c }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a7d }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a7e }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a7f }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a80 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a81 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a82 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a83 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a84 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a85 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a86 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a87 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a88 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a89 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a8a }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a8b }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a8c }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a8d }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a8e }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a8f }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a90 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a91 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a92 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a93 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a94 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a95 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a96 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a97 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a98 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a99 }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a9a }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a9b }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a9c }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a9d }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a9e }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0a9f }, 0}, /* SmartPhone USB Sync */
+ {{ USB_VENDOR_HTC, 0x0bce }, 0}, /* "High Tech Computer Corp" */
+ {{ USB_VENDOR_HTC, USB_PRODUCT_HTC_PPC6700MODEM }, 0},
+ {{ USB_VENDOR_HTC, USB_PRODUCT_HTC_SMARTPHONE }, 0},
+ {{ USB_VENDOR_HTC, USB_PRODUCT_HTC_WINMOBILE }, 0},
+ {{ USB_VENDOR_JVC, 0x3011 }, 0}, /* JVC USB Sync */
+ {{ USB_VENDOR_JVC, 0x3012 }, 0}, /* JVC USB Sync */
+ {{ USB_VENDOR_LG, 0x9c01 }, 0}, /* LGE USB Sync */
+ {{ USB_VENDOR_MICROSOFT, 0x00ce }, 0}, /* Microsoft USB Sync */
+ {{ USB_VENDOR_MICROSOFT, 0x0400 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0401 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0402 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0403 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0404 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0405 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0406 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0407 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0408 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0409 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x040a }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x040b }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x040c }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x040d }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x040e }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x040f }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0410 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0411 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0412 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0413 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0414 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0415 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0416 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0417 }, 0}, /* Windows Pocket PC 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x0432 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0433 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0434 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0435 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0436 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0437 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0438 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0439 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x043a }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x043b }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x043c }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x043d }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x043e }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x043f }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0440 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0441 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0442 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0443 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0444 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0445 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0446 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0447 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0448 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0449 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x044a }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x044b }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x044c }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x044d }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x044e }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x044f }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0450 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0451 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0452 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0453 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0454 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0455 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0456 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0457 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0458 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0459 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x045a }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x045b }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x045c }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x045d }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x045e }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x045f }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0460 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0461 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0462 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0463 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0464 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0465 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0466 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0467 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0468 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0469 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x046a }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x046b }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x046c }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x046d }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x046e }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x046f }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0470 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0471 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0472 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0473 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0474 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0475 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0476 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0477 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0478 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x0479 }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x047a }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x047b }, 0}, /* Windows Pocket PC 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04c8 }, 0}, /* Windows Smartphone 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x04c9 }, 0}, /* Windows Smartphone 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x04ca }, 0}, /* Windows Smartphone 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x04cb }, 0}, /* Windows Smartphone 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x04cc }, 0}, /* Windows Smartphone 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x04cd }, 0}, /* Windows Smartphone 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x04ce }, 0}, /* Windows Smartphone 2002 */
+ {{ USB_VENDOR_MICROSOFT, 0x04d7 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04d8 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04d9 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04da }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04db }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04dc }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04dd }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04de }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04df }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e0 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e1 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e2 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e3 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e4 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e5 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e6 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e7 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e8 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04e9 }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MICROSOFT, 0x04ea }, 0}, /* Windows Smartphone 2003 */
+ {{ USB_VENDOR_MOTOROLA2, 0x4204 }, 0}, /* Motorola MPx200 Smartphone */
+ {{ USB_VENDOR_MOTOROLA2, 0x4214 }, 0}, /* Motorola MPc GSM */
+ {{ USB_VENDOR_MOTOROLA2, 0x4224 }, 0}, /* Motorola MPx220 Smartphone */
+ {{ USB_VENDOR_MOTOROLA2, 0x4234 }, 0}, /* Motorola MPc CDMA */
+ {{ USB_VENDOR_MOTOROLA2, 0x4244 }, 0}, /* Motorola MPx100 Smartphone */
+ {{ USB_VENDOR_NEC, 0x00d5 }, 0}, /* NEC USB Sync */
+ {{ USB_VENDOR_NEC, 0x00d6 }, 0}, /* NEC USB Sync */
+ {{ USB_VENDOR_NEC, 0x00d7 }, 0}, /* NEC USB Sync */
+ {{ USB_VENDOR_NEC, 0x8024 }, 0}, /* NEC USB Sync */
+ {{ USB_VENDOR_NEC, 0x8025 }, 0}, /* NEC USB Sync */
+ {{ USB_VENDOR_PANASONIC, 0x2500 }, 0}, /* Panasonic USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x5f00 }, 0}, /* Samsung NEXiO USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x5f01 }, 0}, /* Samsung NEXiO USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x5f02 }, 0}, /* Samsung NEXiO USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x5f03 }, 0}, /* Samsung NEXiO USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x5f04 }, 0}, /* Samsung NEXiO USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x6611 }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x6613 }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x6615 }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x6617 }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x6619 }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x661b }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x662e }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x6630 }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SAMSUNG, 0x6632 }, 0}, /* Samsung MITs USB Sync */
+ {{ USB_VENDOR_SHARP, 0x9102 }, 0}, /* SHARP WS003SH USB Modem */
+ {{ USB_VENDOR_SHARP, 0x9121 }, 0}, /* SHARP WS004SH USB Modem */
+ {{ USB_VENDOR_SHARP, 0x9151 }, 0}, /* SHARP S01SH USB Modem */
+ {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ES }, 0},
+ {{ USB_VENDOR_SYMBOL, 0x2000 }, 0}, /* Symbol USB Sync */
+ {{ USB_VENDOR_SYMBOL, 0x2001 }, 0}, /* Symbol USB Sync 0x2001 */
+ {{ USB_VENDOR_SYMBOL, 0x2002 }, 0}, /* Symbol USB Sync 0x2002 */
+ {{ USB_VENDOR_SYMBOL, 0x2003 }, 0}, /* Symbol USB Sync 0x2003 */
+ {{ USB_VENDOR_SYMBOL, 0x2004 }, 0}, /* Symbol USB Sync 0x2004 */
+ {{ USB_VENDOR_SYMBOL, 0x2005 }, 0}, /* Symbol USB Sync 0x2005 */
+ {{ USB_VENDOR_SYMBOL, 0x2006 }, 0}, /* Symbol USB Sync 0x2006 */
+ {{ USB_VENDOR_SYMBOL, 0x2007 }, 0}, /* Symbol USB Sync 0x2007 */
+ {{ USB_VENDOR_SYMBOL, 0x2008 }, 0}, /* Symbol USB Sync 0x2008 */
+ {{ USB_VENDOR_SYMBOL, 0x2009 }, 0}, /* Symbol USB Sync 0x2009 */
+ {{ USB_VENDOR_SYMBOL, 0x200a }, 0}, /* Symbol USB Sync 0x200a */
+ {{ USB_VENDOR_TOSHIBA, 0x0700 }, 0}, /* TOSHIBA USB Sync 0700 */
+ {{ USB_VENDOR_TOSHIBA, 0x0705 }, 0}, /* TOSHIBA Pocket PC e310 */
+ {{ USB_VENDOR_TOSHIBA, 0x0707 }, 0}, /* TOSHIBA Pocket PC e330 Series */
+ {{ USB_VENDOR_TOSHIBA, 0x0708 }, 0}, /* TOSHIBA Pocket PC e350Series */
+ {{ USB_VENDOR_TOSHIBA, 0x0709 }, 0}, /* TOSHIBA Pocket PC e750 Series */
+ {{ USB_VENDOR_TOSHIBA, 0x070a }, 0}, /* TOSHIBA Pocket PC e400 Series */
+ {{ USB_VENDOR_TOSHIBA, 0x070b }, 0}, /* TOSHIBA Pocket PC e800 Series */
+ {{ USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_POCKETPC_E740 }, 0}, /* TOSHIBA Pocket PC e740 */
+ {{ USB_VENDOR_VIEWSONIC, 0x0ed9 }, 0}, /* ViewSonic Color Pocket PC V35 */
+ {{ USB_VENDOR_VIEWSONIC, 0x1527 }, 0}, /* ViewSonic Color Pocket PC V36 */
+ {{ USB_VENDOR_VIEWSONIC, 0x1529 }, 0}, /* ViewSonic Color Pocket PC V37 */
+ {{ USB_VENDOR_VIEWSONIC, 0x152b }, 0}, /* ViewSonic Color Pocket PC V38 */
+ {{ USB_VENDOR_VIEWSONIC, 0x152e }, 0}, /* ViewSonic Pocket PC */
+ {{ USB_VENDOR_VIEWSONIC, 0x1921 }, 0}, /* ViewSonic Communicator Pocket PC */
+ {{ USB_VENDOR_VIEWSONIC, 0x1922 }, 0}, /* ViewSonic Smartphone */
+ {{ USB_VENDOR_VIEWSONIC, 0x1923 }, 0}, /* ViewSonic Pocket PC V30 */
+};
+
+#define uipaq_lookup(v, p) ((const struct uipaq_type *)usb_lookup(uipaq_devs, v, p))
+
+static int
+uipaq_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ DPRINTFN(20,("uipaq: vendor=0x%x, product=0x%x\n",
+ uaa->vendor, uaa->product));
+
+ return (uipaq_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+static int
+uipaq_attach(device_t self)
+{
+ usb_device_request_t req;
+ struct uipaq_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+ usbd_status err;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+
+ DPRINTFN(10,("\nuipaq_attach: sc=%p\n", sc));
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_no(dev, UIPAQ_CONFIG_NO, 1);
+ if (err) {
+ device_printf(ucom->sc_dev,
+ "failed to set configuration: %s\n", usbd_errstr(err));
+ goto bad;
+ }
+
+ err = usbd_device2interface_handle(dev, UIPAQ_IFACE_INDEX, &iface);
+ if (err) {
+ device_printf(ucom->sc_dev, "failed to get interface: %s\n",
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ sc->sc_flags = uipaq_lookup(uaa->vendor, uaa->product)->uv_flags;
+ id = usbd_get_interface_descriptor(iface);
+ ucom->sc_iface = iface;
+ ucom->sc_ibufsize = UIPAQIBUFSIZE;
+ ucom->sc_obufsize = UIPAQOBUFSIZE;
+ ucom->sc_ibufsizepad = UIPAQIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uipaq_callback;
+ ucom->sc_parent = sc;
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ for (i=0; i<id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ device_printf(ucom->sc_dev,
+ "no endpoint descriptor for %d\n", i);
+ goto bad;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+ if (ucom->sc_bulkin_no == -1 || ucom->sc_bulkout_no == -1) {
+ device_printf(ucom->sc_dev,
+ "no proper endpoints found (%d,%d)\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no);
+ return (ENXIO);
+ }
+ /*
+ * Send magic bytes, cribbed from Linux ipaq driver that claims
+ * to have sniffed them from Win98.
+ */
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, UCDC_LINE_DTR);
+ USETW(req.wIndex, 0x0);
+ USETW(req.wLength, 0);
+ for (i = 0; i < 100; i++) {
+ err = usbd_do_request_flags(ucom->sc_udev, &req, NULL, 0, NULL, 100);
+ if (!err)
+ break;
+ usbd_delay_ms(dev, 1000);
+ }
+ ucom_attach(&sc->sc_ucom);
+ return (0);
+bad:
+ DPRINTF(("uipaq_attach: ATTACH ERROR\n"));
+ ucom->sc_dying = 1;
+ return (ENXIO);
+}
+
+void
+uipaq_dtr(struct uipaq_softc* sc, int onoff)
+{
+ usb_device_request_t req;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usbd_status err;
+ int retries = 3;
+
+ DPRINTF(("%s: uipaq_dtr: onoff=%x\n", device_get_nameunit(ucom->sc_dev), onoff));
+
+ /* Avoid sending unnecessary requests */
+ if (onoff && (sc->sc_lcr & UCDC_LINE_DTR))
+ return;
+ if (!onoff && !(sc->sc_lcr & UCDC_LINE_DTR))
+ return;
+
+ /* Other parameters depend on reg */
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ sc->sc_lcr = onoff ? sc->sc_lcr | UCDC_LINE_DTR : sc->sc_lcr & ~UCDC_LINE_DTR;
+ USETW(req.wValue, sc->sc_lcr);
+ USETW(req.wIndex, 0x0);
+ USETW(req.wLength, 0);
+
+ /* Fire off the request a few times if necessary */
+ while (retries) {
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (!err)
+ break;
+ retries--;
+ }
+}
+
+void
+uipaq_rts(struct uipaq_softc* sc, int onoff)
+{
+ usb_device_request_t req;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usbd_status err;
+ int retries = 3;
+
+ DPRINTF(("%s: uipaq_rts: onoff=%x\n", device_get_nameunit(ucom->sc_dev), onoff));
+
+ /* Avoid sending unnecessary requests */
+ if (onoff && (sc->sc_lcr & UCDC_LINE_RTS)) return;
+ if (!onoff && !(sc->sc_lcr & UCDC_LINE_RTS)) return;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ sc->sc_lcr = onoff ? sc->sc_lcr | UCDC_LINE_RTS : sc->sc_lcr & ~UCDC_LINE_RTS;
+ USETW(req.wValue, sc->sc_lcr);
+ USETW(req.wIndex, 0x0);
+ USETW(req.wLength, 0);
+
+ while (retries) {
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (!err)
+ break;
+ retries--;
+ }
+}
+
+void
+uipaq_break(struct uipaq_softc* sc, int onoff)
+{
+ usb_device_request_t req;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ usbd_status err;
+ int retries = 3;
+
+ DPRINTF(("%s: uipaq_break: onoff=%x\n", device_get_nameunit(ucom->sc_dev), onoff));
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+
+ USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+ USETW(req.wIndex, 0x0);
+ USETW(req.wLength, 0);
+
+ while (retries) {
+ err = usbd_do_request(ucom->sc_udev, &req, NULL);
+ if (!err)
+ break;
+ retries--;
+ }
+}
+
+void
+uipaq_set(void *addr, int portno, int reg, int onoff)
+{
+ struct uipaq_softc* sc = addr;
+ struct ucom_softc *ucom = &sc->sc_ucom;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ uipaq_dtr(addr, onoff);
+ break;
+ case UCOM_SET_RTS:
+ uipaq_rts(addr, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ uipaq_break(addr, onoff);
+ break;
+ default:
+ printf("%s: unhandled set request: reg=%x onoff=%x\n",
+ device_get_nameunit(ucom->sc_dev), reg, onoff);
+ return;
+ }
+}
+
+int
+uipaq_detach(device_t self)
+{
+ struct uipaq_softc *sc = device_get_softc(self);
+ struct ucom_softc *ucom = &sc->sc_ucom;
+ int rv = 0;
+
+ DPRINTF(("uipaq_detach: sc=%p flags=%d\n", sc, flags));
+ ucom->sc_dying = 1;
+
+ rv = ucom_detach(ucom);
+
+ return (rv);
+}
+
+static device_method_t uipaq_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uipaq_match),
+ DEVMETHOD(device_attach, uipaq_attach),
+ DEVMETHOD(device_detach, uipaq_detach),
+
+ { 0, 0 }
+};
+static driver_t uipaq_driver = {
+ "ucom",
+ uipaq_methods,
+ sizeof (struct uipaq_softc)
+};
+
+DRIVER_MODULE(uipaq, uhub, uipaq_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uipaq, usb, 1, 1, 1);
+MODULE_DEPEND(uipaq, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
diff --git a/sys/legacy/dev/usb/ukbd.c b/sys/legacy/dev/usb/ukbd.c
new file mode 100644
index 0000000..5a7c9fc
--- /dev/null
+++ b/sys/legacy/dev/usb/ukbd.c
@@ -0,0 +1,1538 @@
+/*-
+ * 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include "opt_compat.h"
+#include "opt_kbd.h"
+#include "opt_ukbd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ioccom.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/file.h>
+#include <sys/limits.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+#include <dev/usb/hid.h>
+
+#include <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+
+#define UKBD_EMULATE_ATSCANCODE 1
+
+#define DRIVER_NAME "ukbd"
+
+#define delay(d) DELAY(d)
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ukbddebug) printf x
+#define DPRINTFN(n,x) if (ukbddebug>(n)) printf x
+int ukbddebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RW,
+ &ukbddebug, 0, "ukbd debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UPROTO_BOOT_KEYBOARD 1
+
+#define NKEYCODE 6
+
+struct ukbd_data {
+ u_int8_t modifiers;
+#define MOD_CONTROL_L 0x01
+#define MOD_CONTROL_R 0x10
+#define MOD_SHIFT_L 0x02
+#define MOD_SHIFT_R 0x20
+#define MOD_ALT_L 0x04
+#define MOD_ALT_R 0x40
+#define MOD_WIN_L 0x08
+#define MOD_WIN_R 0x80
+ u_int8_t reserved;
+ u_int8_t keycode[NKEYCODE];
+};
+
+#define MAXKEYS (NMOD+2*NKEYCODE)
+
+typedef struct ukbd_softc {
+ device_t sc_dev; /* base device */
+} ukbd_softc_t;
+
+#define UKBD_CHUNK 128 /* chunk size for read */
+#define UKBD_BSIZE 1020 /* buffer size */
+
+typedef void usbd_intr_t(usbd_xfer_handle, usbd_private_handle, usbd_status);
+typedef void usbd_disco_t(void *);
+
+static usbd_intr_t ukbd_intr;
+static int ukbd_driver_load(module_t mod, int what, void *arg);
+
+static device_probe_t ukbd_match;
+static device_attach_t ukbd_attach;
+static device_detach_t ukbd_detach;
+static device_resume_t ukbd_resume;
+
+static device_method_t ukbd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ukbd_match),
+ DEVMETHOD(device_attach, ukbd_attach),
+ DEVMETHOD(device_detach, ukbd_detach),
+ DEVMETHOD(device_resume, ukbd_resume),
+
+ { 0, 0 }
+};
+
+static driver_t ukbd_driver = {
+ "ukbd",
+ ukbd_methods,
+ sizeof(struct ukbd_softc)
+};
+
+static devclass_t ukbd_devclass;
+
+MODULE_DEPEND(ukbd, usb, 1, 1, 1);
+DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0);
+
+static int
+ukbd_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ keyboard_switch_t *sw;
+ void *arg[2];
+ int unit = device_get_unit(self);
+
+ sw = kbd_get_switch(DRIVER_NAME);
+ if (sw == NULL)
+ return (UMATCH_NONE);
+
+ arg[0] = (void *)uaa;
+ arg[1] = (void *)ukbd_intr;
+ if ((*sw->probe)(unit, (void *)arg, 0))
+ return (UMATCH_NONE);
+
+ if (usbd_get_quirks(uaa->device)->uq_flags & UQ_KBD_IGNORE)
+ return (UMATCH_NONE);
+
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+}
+
+static int
+ukbd_attach(device_t self)
+{
+ struct ukbd_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+
+ keyboard_switch_t *sw;
+ keyboard_t *kbd;
+ void *arg[2];
+ int unit = device_get_unit(self);
+
+ sc->sc_dev = self;
+ sw = kbd_get_switch(DRIVER_NAME);
+ if (sw == NULL)
+ return ENXIO;
+
+ id = usbd_get_interface_descriptor(iface);
+
+ arg[0] = (void *)uaa;
+ arg[1] = (void *)ukbd_intr;
+ kbd = NULL;
+ if ((*sw->probe)(unit, (void *)arg, 0))
+ return ENXIO;
+ if ((*sw->init)(unit, &kbd, (void *)arg, 0))
+ return ENXIO;
+ (*sw->enable)(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(kbd))
+ return ENXIO;
+#endif
+ if (bootverbose)
+ (*sw->diag)(kbd, bootverbose);
+
+ return 0;
+}
+
+int
+ukbd_detach(device_t self)
+{
+ keyboard_t *kbd;
+ int error;
+
+ kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME,
+ device_get_unit(self)));
+ if (kbd == NULL) {
+ DPRINTF(("%s: keyboard not attached!?\n", device_get_nameunit(self)));
+ return ENXIO;
+ }
+ kbdd_disable(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ error = kbd_detach(kbd);
+ if (error)
+ return error;
+#endif
+ error = kbdd_term(kbd);
+ if (error)
+ return error;
+
+ DPRINTF(("%s: disconnected\n", device_get_nameunit(self)));
+
+ return (0);
+}
+
+static int
+ukbd_resume(device_t self)
+{
+ keyboard_t *kbd;
+
+ kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME,
+ device_get_unit(self)));
+ if (kbd)
+ kbdd_clear_state(kbd);
+ return (0);
+}
+
+void
+ukbd_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ keyboard_t *kbd = (keyboard_t *)addr;
+
+ kbdd_intr(kbd, (void *)status);
+}
+
+#define UKBD_DEFAULT 0
+
+#define KEY_ERROR 0x01
+
+#define KEY_PRESS 0
+#define KEY_RELEASE 0x400
+#define KEY_INDEX(c) ((c) & ~KEY_RELEASE)
+
+#define SCAN_PRESS 0
+#define SCAN_RELEASE 0x80
+#define SCAN_PREFIX_E0 0x100
+#define SCAN_PREFIX_E1 0x200
+#define SCAN_PREFIX_CTL 0x400
+#define SCAN_PREFIX_SHIFT 0x800
+#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL \
+ | SCAN_PREFIX_SHIFT)
+#define SCAN_CHAR(c) ((c) & 0x7f)
+
+#define NMOD 8
+static struct {
+ int mask, key;
+} ukbd_mods[NMOD] = {
+ { MOD_CONTROL_L, 0xe0 },
+ { MOD_CONTROL_R, 0xe4 },
+ { MOD_SHIFT_L, 0xe1 },
+ { MOD_SHIFT_R, 0xe5 },
+ { MOD_ALT_L, 0xe2 },
+ { MOD_ALT_R, 0xe6 },
+ { MOD_WIN_L, 0xe3 },
+ { MOD_WIN_R, 0xe7 },
+};
+
+#define NN 0 /* no translation */
+/*
+ * Translate USB keycodes to AT keyboard scancodes.
+ */
+/*
+ * FIXME: Mac USB keyboard generates:
+ * 0x53: keypad NumLock/Clear
+ * 0x66: Power
+ * 0x67: keypad =
+ * 0x68: F13
+ * 0x69: F14
+ * 0x6a: F15
+ */
+static u_int8_t ukbd_trtab[256] = {
+ 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */
+ 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
+ 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
+ 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
+ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
+ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
+ 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
+ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
+ 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */
+ 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */
+ 97, 100, 95, 69, 91, 55, 74, 78, /* 50 - 57 */
+ 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
+ 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
+ NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */
+ 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
+ 121, 120, NN, NN, NN, NN, NN, 115, /* 80 - 87 */
+ 112, 125, 121, 123, NN, NN, NN, NN, /* 88 - 8F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
+ 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
+};
+
+typedef struct ukbd_state {
+ usbd_interface_handle ks_iface; /* interface */
+ usbd_pipe_handle ks_intrpipe; /* interrupt pipe */
+ struct usb_attach_arg *ks_uaa;
+ int ks_ep_addr;
+
+ struct ukbd_data ks_ndata;
+ struct ukbd_data ks_odata;
+ u_long ks_ntime[NKEYCODE];
+ u_long ks_otime[NKEYCODE];
+
+#define INPUTBUFSIZE (NMOD + 2*NKEYCODE)
+ u_int ks_input[INPUTBUFSIZE]; /* input buffer */
+ int ks_inputs;
+ int ks_inputhead;
+ int ks_inputtail;
+
+ int ks_ifstate;
+#define INTRENABLED (1 << 0)
+#define DISCONNECTED (1 << 1)
+
+ struct callout ks_timeout_handle;
+
+ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+ int ks_flags; /* flags */
+#define COMPOSE (1 << 0)
+ int ks_polling;
+ int ks_state; /* shift/lock key state */
+ int ks_accents; /* accent key index (> 0) */
+ u_int ks_composed_char; /* composed char code (> 0) */
+#ifdef UKBD_EMULATE_ATSCANCODE
+ u_int ks_buffered_char[2];
+ u_int8_t ks_leds; /* store for async led requests */
+#endif
+} ukbd_state_t;
+
+/* keyboard driver declaration */
+static int ukbd_configure(int flags);
+static kbd_probe_t ukbd_probe;
+static kbd_init_t ukbd_init;
+static kbd_term_t ukbd_term;
+static kbd_intr_t ukbd_interrupt;
+static kbd_test_if_t ukbd_test_if;
+static kbd_enable_t ukbd_enable;
+static kbd_disable_t ukbd_disable;
+static kbd_read_t ukbd_read;
+static kbd_check_t ukbd_check;
+static kbd_read_char_t ukbd_read_char;
+static kbd_check_char_t ukbd_check_char;
+static kbd_ioctl_t ukbd_ioctl;
+static kbd_lock_t ukbd_lock;
+static kbd_clear_state_t ukbd_clear_state;
+static kbd_get_state_t ukbd_get_state;
+static kbd_set_state_t ukbd_set_state;
+static kbd_poll_mode_t ukbd_poll;
+
+keyboard_switch_t ukbdsw = {
+ ukbd_probe,
+ ukbd_init,
+ ukbd_term,
+ ukbd_interrupt,
+ ukbd_test_if,
+ ukbd_enable,
+ ukbd_disable,
+ ukbd_read,
+ ukbd_check,
+ ukbd_read_char,
+ ukbd_check_char,
+ ukbd_ioctl,
+ ukbd_lock,
+ ukbd_clear_state,
+ ukbd_get_state,
+ ukbd_set_state,
+ genkbd_get_fkeystr,
+ ukbd_poll,
+ genkbd_diag,
+};
+
+KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
+
+/* local functions */
+static int ukbd_enable_intr(keyboard_t *kbd, int on,
+ usbd_intr_t *func);
+static void ukbd_timeout(void *arg);
+
+static int ukbd_getc(ukbd_state_t *state, int wait);
+static int probe_keyboard(struct usb_attach_arg *uaa, int flags);
+static int init_keyboard(ukbd_state_t *state, int *type,
+ int flags);
+static void set_leds(ukbd_state_t *state, int leds);
+static int set_typematic(keyboard_t *kbd, int code);
+#ifdef UKBD_EMULATE_ATSCANCODE
+static int keycode2scancode(int keycode, int shift, int up);
+#endif
+
+/* local variables */
+
+/* the initial key map, accent map and fkey strings */
+#if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE)
+#define KBD_DFLT_KEYMAP
+#include "ukbdmap.h"
+#endif
+#include <dev/kbd/kbdtables.h>
+
+/* structures for the default keyboard */
+static keyboard_t default_kbd;
+static ukbd_state_t default_kbd_state;
+static keymap_t default_keymap;
+static accentmap_t default_accentmap;
+static fkeytab_t default_fkeytab[NUM_FKEYS];
+
+/*
+ * The back door to the keyboard driver!
+ * This function is called by the console driver, via the kbdio module,
+ * to tickle keyboard drivers when the low-level console is being initialized.
+ * Almost nothing in the kernel has been initialied yet. Try to probe
+ * keyboards if possible.
+ * NOTE: because of the way the low-level conole is initialized, this routine
+ * may be called more than once!!
+ */
+static int
+ukbd_configure(int flags)
+{
+ return 0;
+
+#if 0 /* not yet */
+ keyboard_t *kbd;
+ device_t device;
+ struct usb_attach_arg *uaa;
+ void *arg[2];
+
+ device = devclass_get_device(ukbd_devclass, UKBD_DEFAULT);
+ if (device == NULL)
+ return 0;
+ uaa = (struct usb_attach_arg *)device_get_ivars(device);
+ if (uaa == NULL)
+ return 0;
+
+ /* probe the default keyboard */
+ arg[0] = (void *)uaa;
+ arg[1] = (void *)ukbd_intr;
+ kbd = NULL;
+ if (ukbd_probe(UKBD_DEFAULT, arg, flags))
+ return 0;
+ if (ukbd_init(UKBD_DEFAULT, &kbd, arg, flags))
+ return 0;
+
+ /* return the number of found keyboards */
+ return 1;
+#endif
+}
+
+/* low-level functions */
+
+/* detect a keyboard */
+static int
+ukbd_probe(int unit, void *arg, int flags)
+{
+ void **data;
+ struct usb_attach_arg *uaa;
+
+ data = (void **)arg;
+ uaa = (struct usb_attach_arg *)data[0];
+
+ /* XXX */
+ if (unit == UKBD_DEFAULT) {
+ if (KBD_IS_PROBED(&default_kbd))
+ return 0;
+ }
+ if (probe_keyboard(uaa, flags))
+ return ENXIO;
+ return 0;
+}
+
+/* reset and initialize the device */
+static int
+ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ keyboard_t *kbd;
+ ukbd_state_t *state;
+ keymap_t *keymap;
+ accentmap_t *accmap;
+ fkeytab_t *fkeymap;
+ int fkeymap_size;
+ void **data = (void **)arg;
+ struct usb_attach_arg *uaa = (struct usb_attach_arg *)data[0];
+
+ /* XXX */
+ if (unit == UKBD_DEFAULT) {
+ *kbdp = kbd = &default_kbd;
+ if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd))
+ return 0;
+ state = &default_kbd_state;
+ keymap = &default_keymap;
+ accmap = &default_accentmap;
+ fkeymap = default_fkeytab;
+ fkeymap_size =
+ sizeof(default_fkeytab)/sizeof(default_fkeytab[0]);
+ } else if (*kbdp == NULL) {
+ *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT);
+ if (kbd == NULL)
+ return ENOMEM;
+ bzero(kbd, sizeof(*kbd));
+ state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT);
+ keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT);
+ accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT);
+ fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT);
+ fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
+ if ((state == NULL) || (keymap == NULL) || (accmap == NULL)
+ || (fkeymap == NULL)) {
+ if (state != NULL)
+ free(state, M_DEVBUF);
+ if (keymap != NULL)
+ free(keymap, M_DEVBUF);
+ if (accmap != NULL)
+ free(accmap, M_DEVBUF);
+ if (fkeymap != NULL)
+ free(fkeymap, M_DEVBUF);
+ free(kbd, M_DEVBUF);
+ return ENOMEM;
+ }
+ } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
+ return 0;
+ } else {
+ kbd = *kbdp;
+ state = (ukbd_state_t *)kbd->kb_data;
+ keymap = kbd->kb_keymap;
+ accmap = kbd->kb_accentmap;
+ fkeymap = kbd->kb_fkeytab;
+ fkeymap_size = kbd->kb_fkeytab_size;
+ }
+
+ if (!KBD_IS_PROBED(kbd)) {
+ kbd_init_struct(kbd, DRIVER_NAME, KB_OTHER, unit, flags, 0, 0);
+ bzero(state, sizeof(*state));
+ bcopy(&key_map, keymap, sizeof(key_map));
+ bcopy(&accent_map, accmap, sizeof(accent_map));
+ bcopy(fkey_tab, fkeymap,
+ imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
+ kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
+ kbd->kb_data = (void *)state;
+
+ if (probe_keyboard(uaa, flags))
+ return ENXIO;
+ else
+ KBD_FOUND_DEVICE(kbd);
+ ukbd_clear_state(kbd);
+ state->ks_mode = K_XLATE;
+ state->ks_iface = uaa->iface;
+ state->ks_uaa = uaa;
+ state->ks_ifstate = 0;
+ callout_init(&state->ks_timeout_handle, 0);
+ /*
+ * FIXME: set the initial value for lock keys in ks_state
+ * according to the BIOS data?
+ */
+ KBD_PROBE_DONE(kbd);
+ }
+ if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
+ if (KBD_HAS_DEVICE(kbd)
+ && init_keyboard((ukbd_state_t *)kbd->kb_data,
+ &kbd->kb_type, kbd->kb_flags)) {
+ kbd->kb_flags = 0;
+ /* XXX: Missing free()'s */
+ return ENXIO;
+ }
+ ukbd_ioctl(kbd, KDSETLED, (caddr_t)&(state->ks_state));
+ KBD_INIT_DONE(kbd);
+ }
+ if (!KBD_IS_CONFIGURED(kbd)) {
+ if (kbd_register(kbd) < 0) {
+ kbd->kb_flags = 0;
+ /* XXX: Missing free()'s */
+ return ENXIO;
+ }
+ if (ukbd_enable_intr(kbd, TRUE, (usbd_intr_t *)data[1]) == 0)
+ ukbd_timeout((void *)kbd);
+ KBD_CONFIG_DONE(kbd);
+ }
+
+ return 0;
+}
+
+static int
+ukbd_enable_intr(keyboard_t *kbd, int on, usbd_intr_t *func)
+{
+ ukbd_state_t *state = (ukbd_state_t *)kbd->kb_data;
+ usbd_status err;
+
+ if (on) {
+ /* Set up interrupt pipe. */
+ if (state->ks_ifstate & INTRENABLED)
+ return EBUSY;
+
+ state->ks_ifstate |= INTRENABLED;
+ err = usbd_open_pipe_intr(state->ks_iface, state->ks_ep_addr,
+ USBD_SHORT_XFER_OK,
+ &state->ks_intrpipe, kbd,
+ &state->ks_ndata,
+ sizeof(state->ks_ndata), func,
+ USBD_DEFAULT_INTERVAL);
+ if (err)
+ return (EIO);
+ } else {
+ /* Disable interrupts. */
+ usbd_abort_pipe(state->ks_intrpipe);
+ usbd_close_pipe(state->ks_intrpipe);
+
+ state->ks_ifstate &= ~INTRENABLED;
+ }
+
+ return (0);
+}
+
+/* finish using this keyboard */
+static int
+ukbd_term(keyboard_t *kbd)
+{
+ ukbd_state_t *state;
+ int error;
+ int s;
+
+ s = splusb();
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ DPRINTF(("ukbd_term: ks_ifstate=0x%x\n", state->ks_ifstate));
+
+ callout_stop(&state->ks_timeout_handle);
+
+ if (state->ks_ifstate & INTRENABLED)
+ ukbd_enable_intr(kbd, FALSE, NULL);
+ if (state->ks_ifstate & INTRENABLED) {
+ splx(s);
+ DPRINTF(("ukbd_term: INTRENABLED!\n"));
+ return ENXIO;
+ }
+
+ error = kbd_unregister(kbd);
+ DPRINTF(("ukbd_term: kbd_unregister() %d\n", error));
+ if (error == 0) {
+ kbd->kb_flags = 0;
+ if (kbd != &default_kbd) {
+ free(kbd->kb_keymap, M_DEVBUF);
+ free(kbd->kb_accentmap, M_DEVBUF);
+ free(kbd->kb_fkeytab, M_DEVBUF);
+ free(state, M_DEVBUF);
+ free(kbd, M_DEVBUF);
+ }
+ }
+
+ splx(s);
+ return error;
+}
+
+
+/* keyboard interrupt routine */
+
+static void
+ukbd_timeout(void *arg)
+{
+ keyboard_t *kbd;
+ ukbd_state_t *state;
+ int s;
+
+ kbd = (keyboard_t *)arg;
+ state = (ukbd_state_t *)kbd->kb_data;
+ s = splusb();
+ kbdd_intr(kbd, (void *)USBD_NORMAL_COMPLETION);
+ callout_reset(&state->ks_timeout_handle, hz / 40, ukbd_timeout, arg);
+ splx(s);
+}
+
+static int
+ukbd_interrupt(keyboard_t *kbd, void *arg)
+{
+ usbd_status status = (usbd_status)arg;
+ ukbd_state_t *state;
+ struct ukbd_data *ud;
+ struct timeval tv;
+ u_long now;
+ int mod, omod;
+ int key, c;
+ int i, j;
+
+ DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
+ if (status == USBD_CANCELLED)
+ return 0;
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ ud = &state->ks_ndata;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ukbd_intr: status=%d\n", status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(state->ks_intrpipe);
+ return 0;
+ }
+
+ if (ud->keycode[0] == KEY_ERROR)
+ return 0; /* ignore */
+
+ getmicrouptime(&tv);
+ now = (u_long)tv.tv_sec*1000 + (u_long)tv.tv_usec/1000;
+
+#define ADDKEY1(c) \
+ if (state->ks_inputs < INPUTBUFSIZE) { \
+ state->ks_input[state->ks_inputtail] = (c); \
+ ++state->ks_inputs; \
+ state->ks_inputtail = (state->ks_inputtail + 1)%INPUTBUFSIZE; \
+ }
+
+ mod = ud->modifiers;
+ omod = state->ks_odata.modifiers;
+ if (mod != omod) {
+ for (i = 0; i < NMOD; i++)
+ if (( mod & ukbd_mods[i].mask) !=
+ (omod & ukbd_mods[i].mask))
+ ADDKEY1(ukbd_mods[i].key |
+ (mod & ukbd_mods[i].mask
+ ? KEY_PRESS : KEY_RELEASE));
+ }
+
+ /* Check for released keys. */
+ for (i = 0; i < NKEYCODE; i++) {
+ key = state->ks_odata.keycode[i];
+ if (key == 0)
+ continue;
+ for (j = 0; j < NKEYCODE; j++) {
+ if (ud->keycode[j] == 0)
+ continue;
+ if (key == ud->keycode[j])
+ goto rfound;
+ }
+ ADDKEY1(key | KEY_RELEASE);
+ rfound:
+ ;
+ }
+
+ /* Check for pressed keys. */
+ for (i = 0; i < NKEYCODE; i++) {
+ key = ud->keycode[i];
+ if (key == 0)
+ continue;
+ state->ks_ntime[i] = now + kbd->kb_delay1;
+ for (j = 0; j < NKEYCODE; j++) {
+ if (state->ks_odata.keycode[j] == 0)
+ continue;
+ if (key == state->ks_odata.keycode[j]) {
+ state->ks_ntime[i] = state->ks_otime[j];
+ if (state->ks_otime[j] > now)
+ goto pfound;
+ state->ks_ntime[i] = now + kbd->kb_delay2;
+ break;
+ }
+ }
+ ADDKEY1(key | KEY_PRESS);
+ /*
+ * If any other key is presently down, force its repeat to be
+ * well in the future (100s). This makes the last key to be
+ * pressed do the autorepeat.
+ */
+ for (j = 0; j < NKEYCODE; j++) {
+ if (j != i)
+ state->ks_ntime[j] = now + 100 * 1000;
+ }
+ pfound:
+ ;
+ }
+
+ state->ks_odata = *ud;
+ bcopy(state->ks_ntime, state->ks_otime, sizeof(state->ks_ntime));
+ if (state->ks_inputs <= 0)
+ return 0;
+
+#ifdef USB_DEBUG
+ for (i = state->ks_inputhead, j = 0; j < state->ks_inputs; ++j,
+ i = (i + 1)%INPUTBUFSIZE) {
+ c = state->ks_input[i];
+ DPRINTF(("0x%x (%d) %s\n", c, c,
+ (c & KEY_RELEASE) ? "released":"pressed"));
+ }
+ if (ud->modifiers)
+ DPRINTF(("mod:0x%04x ", ud->modifiers));
+ for (i = 0; i < NKEYCODE; i++) {
+ if (ud->keycode[i])
+ DPRINTF(("%d ", ud->keycode[i]));
+ }
+ DPRINTF(("\n"));
+#endif /* USB_DEBUG */
+
+ if (state->ks_polling)
+ return 0;
+
+ if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
+ /* let the callback function to process the input */
+ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
+ kbd->kb_callback.kc_arg);
+ } else {
+ /* read and discard the input; no one is waiting for it */
+ do {
+ c = ukbd_read_char(kbd, FALSE);
+ } while (c != NOKEY);
+ }
+
+ return 0;
+}
+
+static int
+ukbd_getc(ukbd_state_t *state, int wait)
+{
+ int c;
+ int s;
+
+ if (state->ks_polling) {
+ DPRINTFN(1,("ukbd_getc: polling\n"));
+ s = splusb();
+ while (state->ks_inputs <= 0) {
+ usbd_dopoll(state->ks_iface);
+ if (wait == FALSE)
+ break;
+ }
+ splx(s);
+ }
+ s = splusb();
+ if (state->ks_inputs <= 0) {
+ c = -1;
+ } else {
+ c = state->ks_input[state->ks_inputhead];
+ --state->ks_inputs;
+ state->ks_inputhead = (state->ks_inputhead + 1)%INPUTBUFSIZE;
+ }
+ splx(s);
+ return c;
+}
+
+/* test the interface to the device */
+static int
+ukbd_test_if(keyboard_t *kbd)
+{
+ return 0;
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+static int
+ukbd_enable(keyboard_t *kbd)
+{
+ int s;
+
+ s = splusb();
+ KBD_ACTIVATE(kbd);
+ splx(s);
+ return 0;
+}
+
+/* disallow the access to the device */
+static int
+ukbd_disable(keyboard_t *kbd)
+{
+ int s;
+
+ s = splusb();
+ KBD_DEACTIVATE(kbd);
+ splx(s);
+ return 0;
+}
+
+/* read one byte from the keyboard if it's allowed */
+static int
+ukbd_read(keyboard_t *kbd, int wait)
+{
+ ukbd_state_t *state;
+ int usbcode;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ int keycode;
+ int scancode;
+#endif
+
+ state = (ukbd_state_t *)kbd->kb_data;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (state->ks_buffered_char[0]) {
+ scancode = state->ks_buffered_char[0];
+ if (scancode & SCAN_PREFIX) {
+ state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX;
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ } else {
+ state->ks_buffered_char[0] = state->ks_buffered_char[1];
+ state->ks_buffered_char[1] = 0;
+ return scancode;
+ }
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* XXX */
+ usbcode = ukbd_getc(state, wait);
+ if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1))
+ return -1;
+ ++kbd->kb_count;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN)
+ return -1;
+
+ scancode = keycode2scancode(keycode, state->ks_ndata.modifiers,
+ usbcode & KEY_RELEASE);
+ if (scancode & SCAN_PREFIX) {
+ if (scancode & SCAN_PREFIX_CTL) {
+ state->ks_buffered_char[0] =
+ 0x1d | (scancode & SCAN_RELEASE); /* Ctrl */
+ state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX;
+ } else if (scancode & SCAN_PREFIX_SHIFT) {
+ state->ks_buffered_char[0] =
+ 0x2a | (scancode & SCAN_RELEASE); /* Shift */
+ state->ks_buffered_char[1] =
+ scancode & ~SCAN_PREFIX_SHIFT;
+ } else {
+ state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX;
+ state->ks_buffered_char[1] = 0;
+ }
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ return scancode;
+#else /* !UKBD_EMULATE_ATSCANCODE */
+ return usbcode;
+#endif /* UKBD_EMULATE_ATSCANCODE */
+}
+
+/* check if data is waiting */
+static int
+ukbd_check(keyboard_t *kbd)
+{
+ if (!KBD_IS_ACTIVE(kbd))
+ return FALSE;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (((ukbd_state_t *)kbd->kb_data)->ks_buffered_char[0])
+ return TRUE;
+#endif
+ if (((ukbd_state_t *)kbd->kb_data)->ks_inputs > 0)
+ return TRUE;
+ return FALSE;
+}
+
+/* read char from the keyboard */
+static u_int
+ukbd_read_char(keyboard_t *kbd, int wait)
+{
+ ukbd_state_t *state;
+ u_int action;
+ int usbcode;
+ int keycode;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ int scancode;
+#endif
+
+ state = (ukbd_state_t *)kbd->kb_data;
+next_code:
+ /* do we have a composed char to return? */
+ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
+ action = state->ks_composed_char;
+ state->ks_composed_char = 0;
+ if (action > UCHAR_MAX)
+ return ERRKEY;
+ return action;
+ }
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ /* do we have a pending raw scan code? */
+ if (state->ks_mode == K_RAW) {
+ if (state->ks_buffered_char[0]) {
+ scancode = state->ks_buffered_char[0];
+ if (scancode & SCAN_PREFIX) {
+ state->ks_buffered_char[0] =
+ scancode & ~SCAN_PREFIX;
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ } else {
+ state->ks_buffered_char[0] =
+ state->ks_buffered_char[1];
+ state->ks_buffered_char[1] = 0;
+ return scancode;
+ }
+ }
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* see if there is something in the keyboard port */
+ /* XXX */
+ usbcode = ukbd_getc(state, wait);
+ if (usbcode == -1)
+ return NOKEY;
+ ++kbd->kb_count;
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ /* USB key index -> key code -> AT scan code */
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN)
+ return NOKEY;
+
+ /* return an AT scan code for the K_RAW mode */
+ if (state->ks_mode == K_RAW) {
+ scancode = keycode2scancode(keycode, state->ks_ndata.modifiers,
+ usbcode & KEY_RELEASE);
+ if (scancode & SCAN_PREFIX) {
+ if (scancode & SCAN_PREFIX_CTL) {
+ state->ks_buffered_char[0] =
+ 0x1d | (scancode & SCAN_RELEASE);
+ state->ks_buffered_char[1] =
+ scancode & ~SCAN_PREFIX;
+ } else if (scancode & SCAN_PREFIX_SHIFT) {
+ state->ks_buffered_char[0] =
+ 0x2a | (scancode & SCAN_RELEASE);
+ state->ks_buffered_char[1] =
+ scancode & ~SCAN_PREFIX_SHIFT;
+ } else {
+ state->ks_buffered_char[0] =
+ scancode & ~SCAN_PREFIX;
+ state->ks_buffered_char[1] = 0;
+ }
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ return scancode;
+ }
+#else /* !UKBD_EMULATE_ATSCANCODE */
+ /* return the byte as is for the K_RAW mode */
+ if (state->ks_mode == K_RAW)
+ return usbcode;
+
+ /* USB key index -> key code */
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN)
+ return NOKEY;
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ switch (keycode) {
+ case 0x38: /* left alt (compose key) */
+ if (usbcode & KEY_RELEASE) {
+ if (state->ks_flags & COMPOSE) {
+ state->ks_flags &= ~COMPOSE;
+ if (state->ks_composed_char > UCHAR_MAX)
+ state->ks_composed_char = 0;
+ }
+ } else {
+ if (!(state->ks_flags & COMPOSE)) {
+ state->ks_flags |= COMPOSE;
+ state->ks_composed_char = 0;
+ }
+ }
+ break;
+ /* XXX: I don't like these... */
+ case 0x5c: /* print screen */
+ if (state->ks_flags & ALTS)
+ keycode = 0x54; /* sysrq */
+ break;
+ case 0x68: /* pause/break */
+ if (state->ks_flags & CTLS)
+ keycode = 0x6c; /* break */
+ break;
+ }
+
+ /* return the key code in the K_CODE mode */
+ if (usbcode & KEY_RELEASE)
+ keycode |= SCAN_RELEASE;
+ if (state->ks_mode == K_CODE)
+ return keycode;
+
+ /* compose a character code */
+ if (state->ks_flags & COMPOSE) {
+ switch (keycode) {
+ /* key pressed, process it */
+ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
+ state->ks_composed_char *= 10;
+ state->ks_composed_char += keycode - 0x40;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+ case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
+ state->ks_composed_char *= 10;
+ state->ks_composed_char += keycode - 0x47;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+ case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
+ state->ks_composed_char *= 10;
+ state->ks_composed_char += keycode - 0x4E;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+ case 0x52: /* keypad 0 */
+ state->ks_composed_char *= 10;
+ if (state->ks_composed_char > UCHAR_MAX)
+ return ERRKEY;
+ goto next_code;
+
+ /* key released, no interest here */
+ case SCAN_RELEASE | 0x47:
+ case SCAN_RELEASE | 0x48:
+ case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
+ case SCAN_RELEASE | 0x4B:
+ case SCAN_RELEASE | 0x4C:
+ case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
+ case SCAN_RELEASE | 0x4F:
+ case SCAN_RELEASE | 0x50:
+ case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
+ case SCAN_RELEASE | 0x52: /* keypad 0 */
+ goto next_code;
+
+ case 0x38: /* left alt key */
+ break;
+
+ default:
+ if (state->ks_composed_char > 0) {
+ state->ks_flags &= ~COMPOSE;
+ state->ks_composed_char = 0;
+ return ERRKEY;
+ }
+ break;
+ }
+ }
+
+ /* keycode to key action */
+ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
+ keycode & SCAN_RELEASE, &state->ks_state,
+ &state->ks_accents);
+ if (action == NOKEY)
+ goto next_code;
+ else
+ return action;
+}
+
+/* check if char is waiting */
+static int
+ukbd_check_char(keyboard_t *kbd)
+{
+ ukbd_state_t *state;
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return FALSE;
+ state = (ukbd_state_t *)kbd->kb_data;
+ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0))
+ return TRUE;
+ return ukbd_check(kbd);
+}
+
+/* some useful control functions */
+static int
+ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ /* trasnlate LED_XXX bits into the device specific bits */
+ static u_char ledmap[8] = {
+ 0, 2, 1, 3, 4, 6, 5, 7,
+ };
+ ukbd_state_t *state = kbd->kb_data;
+ int s;
+ int i;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ int ival;
+#endif
+
+ s = splusb();
+ switch (cmd) {
+
+ case KDGKBMODE: /* get keyboard mode */
+ *(int *)arg = state->ks_mode;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 7):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*(int *)arg) {
+ case K_XLATE:
+ if (state->ks_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ state->ks_state &= ~LOCK_MASK;
+ state->ks_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (state->ks_mode != *(int *)arg) {
+ ukbd_clear_state(kbd);
+ state->ks_mode = *(int *)arg;
+ }
+ break;
+ default:
+ splx(s);
+ return EINVAL;
+ }
+ break;
+
+ case KDGETLED: /* get keyboard LED */
+ *(int *)arg = KBD_LED_VAL(kbd);
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 66):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETLED: /* set keyboard LED */
+ /* NOTE: lock key state in ks_state won't be changed */
+ if (*(int *)arg & ~LOCK_MASK) {
+ splx(s);
+ return EINVAL;
+ }
+ i = *(int *)arg;
+ /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
+ if (state->ks_mode == K_XLATE &&
+ kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
+ if (i & ALKED)
+ i |= CLKED;
+ else
+ i &= ~CLKED;
+ }
+ if (KBD_HAS_DEVICE(kbd)) {
+ set_leds(state, ledmap[i & LED_MASK]);
+ /* XXX: error check? */
+ }
+ KBD_LED_VAL(kbd) = *(int *)arg;
+ break;
+
+ case KDGKBSTATE: /* get lock key state */
+ *(int *)arg = state->ks_state & LOCK_MASK;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 20):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBSTATE: /* set lock key state */
+ if (*(int *)arg & ~LOCK_MASK) {
+ splx(s);
+ return EINVAL;
+ }
+ state->ks_state &= ~LOCK_MASK;
+ state->ks_state |= *(int *)arg;
+ splx(s);
+ /* set LEDs and quit */
+ return ukbd_ioctl(kbd, KDSETLED, arg);
+
+ case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
+ splx(s);
+ if (!KBD_HAS_DEVICE(kbd))
+ return 0;
+ if (((int *)arg)[1] < 0)
+ return EINVAL;
+ if (((int *)arg)[0] < 0)
+ return EINVAL;
+ else if (((int *)arg)[0] == 0) /* fastest possible value */
+ kbd->kb_delay1 = 200;
+ else
+ kbd->kb_delay1 = ((int *)arg)[0];
+ kbd->kb_delay2 = ((int *)arg)[1];
+ return 0;
+
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 67):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETRAD: /* set keyboard repeat rate (old interface) */
+ splx(s);
+ return set_typematic(kbd, *(int *)arg);
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ case PIO_KEYMAPENT: /* set keyboard translation table entry */
+ case PIO_DEADKEYMAP: /* set accent key translation table */
+ state->ks_accents = 0;
+ /* FALLTHROUGH */
+ default:
+ splx(s);
+ return genkbd_commonioctl(kbd, cmd, arg);
+
+#ifdef USB_DEBUG
+ case USB_SETDEBUG:
+ ukbddebug = *(int *)arg;
+ break;
+#endif
+ }
+
+ splx(s);
+ return 0;
+}
+
+/* lock the access to the keyboard */
+static int
+ukbd_lock(keyboard_t *kbd, int lock)
+{
+ /* XXX ? */
+ return TRUE;
+}
+
+/* clear the internal state of the keyboard */
+static void
+ukbd_clear_state(keyboard_t *kbd)
+{
+ ukbd_state_t *state;
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ state->ks_flags = 0;
+ state->ks_polling = 0;
+ state->ks_state &= LOCK_MASK; /* preserve locking key state */
+ state->ks_accents = 0;
+ state->ks_composed_char = 0;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ state->ks_buffered_char[0] = 0;
+ state->ks_buffered_char[1] = 0;
+#endif
+ bzero(&state->ks_ndata, sizeof(state->ks_ndata));
+ bzero(&state->ks_odata, sizeof(state->ks_odata));
+ bzero(&state->ks_ntime, sizeof(state->ks_ntime));
+ bzero(&state->ks_otime, sizeof(state->ks_otime));
+}
+
+/* save the internal state */
+static int
+ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ if (len == 0)
+ return sizeof(ukbd_state_t);
+ if (len < sizeof(ukbd_state_t))
+ return -1;
+ bcopy(kbd->kb_data, buf, sizeof(ukbd_state_t));
+ return 0;
+}
+
+/* set the internal state */
+static int
+ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ if (len < sizeof(ukbd_state_t))
+ return ENOMEM;
+ bcopy(buf, kbd->kb_data, sizeof(ukbd_state_t));
+ return 0;
+}
+
+static int
+ukbd_poll(keyboard_t *kbd, int on)
+{
+ ukbd_state_t *state;
+ usbd_device_handle dev;
+ int s;
+
+ state = (ukbd_state_t *)kbd->kb_data;
+ usbd_interface2device_handle(state->ks_iface, &dev);
+
+ s = splusb();
+ if (on) {
+ ++state->ks_polling;
+ if (state->ks_polling == 1)
+ usbd_set_polling(dev, on);
+ } else {
+ --state->ks_polling;
+ if (state->ks_polling == 0)
+ usbd_set_polling(dev, on);
+ }
+ splx(s);
+ return 0;
+}
+
+/* local functions */
+
+static int
+probe_keyboard(struct usb_attach_arg *uaa, int flags)
+{
+ usb_interface_descriptor_t *id;
+
+ if (!uaa->iface) /* we attach to ifaces only */
+ return EINVAL;
+
+ /* Check that this is a keyboard that speaks the boot protocol. */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id
+ && id->bInterfaceClass == UICLASS_HID
+ && id->bInterfaceSubClass == UISUBCLASS_BOOT
+ && id->bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)
+ return 0; /* found it */
+
+ return EINVAL;
+}
+
+static int
+init_keyboard(ukbd_state_t *state, int *type, int flags)
+{
+ usb_endpoint_descriptor_t *ed;
+
+ *type = KB_OTHER;
+
+ state->ks_ifstate |= DISCONNECTED;
+
+ ed = usbd_interface2endpoint_descriptor(state->ks_iface, 0);
+ if (!ed) {
+ printf("ukbd: could not read endpoint descriptor\n");
+ return EIO;
+ }
+
+ DPRINTFN(10,("ukbd:init_keyboard: \
+bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
+ ed->bLength, ed->bDescriptorType,
+ UE_GET_ADDR(ed->bEndpointAddress),
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
+ UE_GET_XFERTYPE(ed->bmAttributes),
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
+ UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
+ printf("ukbd: unexpected endpoint\n");
+ return EINVAL;
+ }
+
+ /* Ignore if SETIDLE fails since it is not crucial. */
+ usbd_set_idle(state->ks_iface, 0, 0);
+
+ state->ks_ep_addr = ed->bEndpointAddress;
+ state->ks_ifstate &= ~DISCONNECTED;
+
+ return 0;
+}
+
+static void
+set_leds(ukbd_state_t *state, int leds)
+{
+
+ DPRINTF(("ukbd:set_leds: state=%p leds=%d\n", state, leds));
+ state->ks_leds = leds;
+ usbd_set_report_async(state->ks_iface, UHID_OUTPUT_REPORT, 0,
+ &state->ks_leds, 1);
+}
+
+static int
+set_typematic(keyboard_t *kbd, int code)
+{
+ static int delays[] = { 250, 500, 750, 1000 };
+ static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504 };
+
+ if (code & ~0x7f)
+ return EINVAL;
+ kbd->kb_delay1 = delays[(code >> 5) & 3];
+ kbd->kb_delay2 = rates[code & 0x1f];
+ return 0;
+}
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+static int
+keycode2scancode(int keycode, int shift, int up)
+{
+ static int scan[] = {
+ 0x1c, 0x1d, 0x35,
+ 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */
+ 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f,
+ 0x50, 0x51, 0x52, 0x53,
+ 0x46, /* XXX Pause/Break */
+ 0x5b, 0x5c, 0x5d,
+ /* SUN TYPE 6 USB KEYBOARD */
+ 0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e,
+ 0x20,
+ };
+ int scancode;
+
+ scancode = keycode;
+ if ((keycode >= 89) && (keycode < 89 + sizeof(scan)/sizeof(scan[0])))
+ scancode = scan[keycode - 89] | SCAN_PREFIX_E0;
+ /* Pause/Break */
+ if ((keycode == 104) && !(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))
+ scancode = 0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL;
+ if (shift & (MOD_SHIFT_L | MOD_SHIFT_R))
+ scancode &= ~SCAN_PREFIX_SHIFT;
+ return (scancode | (up ? SCAN_RELEASE : SCAN_PRESS));
+}
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+static int
+ukbd_driver_load(module_t mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ kbd_add_driver(&ukbd_kbd_driver);
+ break;
+ case MOD_UNLOAD:
+ kbd_delete_driver(&ukbd_kbd_driver);
+ break;
+ }
+ return usbd_driver_load(mod, what, arg);
+}
diff --git a/sys/legacy/dev/usb/ulpt.c b/sys/legacy/dev/usb/ulpt.c
new file mode 100644
index 0000000..99a1433
--- /dev/null
+++ b/sys/legacy/dev/usb/ulpt.c
@@ -0,0 +1,815 @@
+/* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2003 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF
+ */
+
+/* XXXimp: need to migrate from devclass_get_softc */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/uio.h>
+#include <sys/conf.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+
+#define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */
+#define STEP hz/4
+
+#define LPTPRI (PZERO+8)
+#define ULPT_BSIZE PAGE_SIZE
+
+#define ULPT_READS_PER_SEC 5
+#define ULPT_READ_TIMO 10
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (ulptdebug) printf x
+#define DPRINTFN(n,x) if (ulptdebug>(n)) printf x
+int ulptdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt");
+SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RW,
+ &ulptdebug, 0, "ulpt debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UR_GET_DEVICE_ID 0
+#define UR_GET_PORT_STATUS 1
+#define UR_SOFT_RESET 2
+
+#define LPS_NERR 0x08 /* printer no error */
+#define LPS_SELECT 0x10 /* printer selected */
+#define LPS_NOPAPER 0x20 /* printer out of paper */
+#define LPS_INVERT (LPS_SELECT|LPS_NERR)
+#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER)
+
+struct ulpt_softc {
+ device_t sc_dev;
+ usbd_device_handle sc_udev; /* device */
+ usbd_interface_handle sc_iface; /* interface */
+ int sc_ifaceno;
+
+ int sc_out;
+ usbd_pipe_handle sc_out_pipe; /* bulk out pipe */
+ usbd_xfer_handle sc_out_xfer;
+ void *sc_out_buf;
+
+ int sc_in;
+ usbd_pipe_handle sc_in_pipe; /* bulk in pipe */
+ usbd_xfer_handle sc_in_xfer;
+ void *sc_in_buf;
+
+ struct callout sc_read_callout;
+ int sc_has_callout;
+
+ u_char sc_state;
+#define ULPT_OPEN 0x01 /* device is open */
+#define ULPT_OBUSY 0x02 /* printer is busy doing output */
+#define ULPT_INIT 0x04 /* waiting to initialize for open */
+ u_char sc_flags;
+#define ULPT_NOPRIME 0x40 /* don't prime on open */
+ u_char sc_laststatus;
+
+ int sc_refcnt;
+ u_char sc_dying;
+
+ struct cdev *dev;
+ struct cdev *dev_noprime;
+};
+
+static d_open_t ulptopen;
+static d_close_t ulptclose;
+static d_write_t ulptwrite;
+static d_read_t ulptread;
+static d_ioctl_t ulptioctl;
+
+
+static struct cdevsw ulpt_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ulptopen,
+ .d_close = ulptclose,
+ .d_write = ulptwrite,
+ .d_read = ulptread,
+ .d_ioctl = ulptioctl,
+ .d_name = "ulpt",
+};
+
+void ulpt_disco(void *);
+
+int ulpt_do_write(struct ulpt_softc *, struct uio *uio, int);
+int ulpt_do_read(struct ulpt_softc *, struct uio *uio, int);
+int ulpt_status(struct ulpt_softc *);
+void ulpt_reset(struct ulpt_softc *);
+int ulpt_statusmsg(u_char, struct ulpt_softc *);
+void ulpt_read_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status);
+void ulpt_tick(void *xsc);
+
+#if 0
+void ieee1284_print_id(char *);
+#endif
+
+#define ULPTUNIT(s) (dev2unit(s) & 0x1f)
+#define ULPTFLAGS(s) (dev2unit(s) & 0xe0)
+
+static device_probe_t ulpt_match;
+static device_attach_t ulpt_attach;
+static device_detach_t ulpt_detach;
+
+static device_method_t ulpt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ulpt_match),
+ DEVMETHOD(device_attach, ulpt_attach),
+ DEVMETHOD(device_detach, ulpt_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ulpt_driver = {
+ "ulpt",
+ ulpt_methods,
+ sizeof(struct ulpt_softc)
+};
+
+static devclass_t ulpt_devclass;
+
+MODULE_DEPEND(umass, usb, 1, 1, 1);
+DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, usbd_driver_load, 0);
+
+static int
+ulpt_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+
+ DPRINTFN(10,("ulpt_match\n"));
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id != NULL &&
+ id->bInterfaceClass == UICLASS_PRINTER &&
+ id->bInterfaceSubClass == UISUBCLASS_PRINTER &&
+ (id->bInterfaceProtocol == UIPROTO_PRINTER_UNI ||
+ id->bInterfaceProtocol == UIPROTO_PRINTER_BI ||
+ id->bInterfaceProtocol == UIPROTO_PRINTER_1284))
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+ return (UMATCH_NONE);
+}
+
+static int
+ulpt_attach(device_t self)
+{
+ struct ulpt_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *ifcd = usbd_get_interface_descriptor(iface);
+ usb_interface_descriptor_t *id, *iend;
+ usb_config_descriptor_t *cdesc;
+ usbd_status err;
+ usb_endpoint_descriptor_t *ed;
+ u_int8_t epcount;
+ int i, altno;
+
+ DPRINTFN(10,("ulpt_attach: sc=%p\n", sc));
+ sc->sc_dev = self;
+
+ /* XXX
+ * Stepping through the alternate settings needs to be abstracted out.
+ */
+ cdesc = usbd_get_config_descriptor(dev);
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+ iend = (usb_interface_descriptor_t *)
+ ((char *)cdesc + UGETW(cdesc->wTotalLength));
+#ifdef DIAGNOSTIC
+ if (ifcd < (usb_interface_descriptor_t *)cdesc ||
+ ifcd >= iend)
+ panic("ulpt: iface desc out of range");
+#endif
+ /* Step through all the descriptors looking for bidir mode */
+ for (id = ifcd, altno = 0;
+ id < iend;
+ id = (void *)((char *)id + id->bLength)) {
+ if (id->bDescriptorType == UDESC_INTERFACE &&
+ id->bInterfaceNumber == ifcd->bInterfaceNumber) {
+ if (id->bInterfaceClass == UICLASS_PRINTER &&
+ id->bInterfaceSubClass == UISUBCLASS_PRINTER &&
+ (id->bInterfaceProtocol == UIPROTO_PRINTER_BI /* ||
+ id->bInterfaceProtocol == UIPROTO_PRINTER_1284 */))
+ goto found;
+ altno++;
+ }
+ }
+ id = ifcd; /* not found, use original */
+ found:
+ if (id != ifcd) {
+ /* Found a new bidir setting */
+ DPRINTF(("ulpt_attach: set altno = %d\n", altno));
+ err = usbd_set_interface(iface, altno);
+ if (err) {
+ printf("%s: setting alternate interface failed\n",
+ device_get_nameunit(sc->sc_dev));
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+ }
+
+ epcount = 0;
+ (void)usbd_endpoint_count(iface, &epcount);
+
+ sc->sc_in = -1;
+ sc->sc_out = -1;
+ for (i = 0; i < epcount; i++) {
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ printf("%s: couldn't get ep %d\n",
+ device_get_nameunit(sc->sc_dev), i);
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_in = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ sc->sc_out = ed->bEndpointAddress;
+ }
+ }
+ if (sc->sc_out == -1) {
+ printf("%s: could not find bulk out endpoint\n",
+ device_get_nameunit(sc->sc_dev));
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ if (usbd_get_quirks(dev)->uq_flags & UQ_BROKEN_BIDIR) {
+ /* This device doesn't handle reading properly. */
+ sc->sc_in = -1;
+ }
+
+ printf("%s: using %s-directional mode\n", device_get_nameunit(sc->sc_dev),
+ sc->sc_in >= 0 ? "bi" : "uni");
+
+ DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_out));
+
+ sc->sc_iface = iface;
+ sc->sc_ifaceno = id->bInterfaceNumber;
+ sc->sc_udev = dev;
+
+#if 0
+/*
+ * This code is disabled because for some mysterious reason it causes
+ * printing not to work. But only sometimes, and mostly with
+ * UHCI and less often with OHCI. *sigh*
+ */
+ {
+ usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+ usb_device_request_t req;
+ int len, alen;
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_DEVICE_ID;
+ USETW(req.wValue, cd->bConfigurationValue);
+ USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting);
+ USETW(req.wLength, sizeof devinfo - 1);
+ err = usbd_do_request_flags(dev, &req, devinfo, USBD_SHORT_XFER_OK,
+ &alen, USBD_DEFAULT_TIMEOUT);
+ if (err) {
+ printf("%s: cannot get device id\n", device_get_nameunit(sc->sc_dev));
+ } else if (alen <= 2) {
+ printf("%s: empty device id, no printer connected?\n",
+ device_get_nameunit(sc->sc_dev));
+ } else {
+ /* devinfo now contains an IEEE-1284 device ID */
+ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff);
+ if (len > sizeof devinfo - 3)
+ len = sizeof devinfo - 3;
+ devinfo[len] = 0;
+ printf("%s: device id <", device_get_nameunit(sc->sc_dev));
+ ieee1284_print_id(devinfo+2);
+ printf(">\n");
+ }
+ }
+#endif
+
+ sc->dev = make_dev(&ulpt_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR, 0644, "ulpt%d", device_get_unit(self));
+ sc->dev_noprime = make_dev(&ulpt_cdevsw,
+ device_get_unit(self)|ULPT_NOPRIME,
+ UID_ROOT, GID_OPERATOR, 0644, "unlpt%d", device_get_unit(self));
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
+
+ return 0;
+}
+
+
+static int
+ulpt_detach(device_t self)
+{
+ struct ulpt_softc *sc = device_get_softc(self);
+ int s;
+
+ DPRINTF(("ulpt_detach: sc=%p\n", sc));
+
+ sc->sc_dying = 1;
+ if (sc->sc_out_pipe != NULL)
+ usbd_abort_pipe(sc->sc_out_pipe);
+ if (sc->sc_in_pipe != NULL)
+ usbd_abort_pipe(sc->sc_in_pipe);
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* There is noone to wake, aborting the pipe is enough */
+ /* Wait for processes to go away. */
+ usb_detach_wait(sc->sc_dev);
+ }
+ splx(s);
+
+ destroy_dev(sc->dev);
+ destroy_dev(sc->dev_noprime);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+
+ return (0);
+}
+
+int
+ulpt_status(struct ulpt_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_char status;
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_PORT_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_udev, &req, &status);
+ DPRINTFN(1, ("ulpt_status: status=0x%02x err=%d\n", status, err));
+ if (!err)
+ return (status);
+ else
+ return (0);
+}
+
+void
+ulpt_reset(struct ulpt_softc *sc)
+{
+ usb_device_request_t req;
+
+ DPRINTFN(1, ("ulpt_reset\n"));
+ req.bRequest = UR_SOFT_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, 0);
+
+ /*
+ * There was a mistake in the USB printer 1.0 spec that gave the
+ * request type as UT_WRITE_CLASS_OTHER; it should have been
+ * UT_WRITE_CLASS_INTERFACE. Many printers use the old one,
+ * so we try both.
+ */
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ if (usbd_do_request(sc->sc_udev, &req, 0)) { /* 1.0 */
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ (void)usbd_do_request(sc->sc_udev, &req, 0); /* 1.1 */
+ }
+}
+#if 0
+static void
+ulpt_input(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct ulpt_softc *sc = priv;
+ u_int32_t count;
+
+ /* Don't loop on errors or 0-length input. */
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ if (status != USBD_NORMAL_COMPLETION || count == 0)
+ return;
+
+ DPRINTFN(2,("ulpt_input: got some data\n"));
+ /* Do it again. */
+ if (xfer == sc->sc_in_xfer1)
+ usbd_transfer(sc->sc_in_xfer2);
+ else
+ usbd_transfer(sc->sc_in_xfer1);
+}
+#endif
+
+int ulptusein = 1;
+
+/*
+ * Reset the printer, then wait until it's selected and not busy.
+ */
+int
+ulptopen(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ u_char flags = ULPTFLAGS(dev);
+ struct ulpt_softc *sc;
+ usbd_status err;
+ int error;
+
+ sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev));
+ if (sc == NULL)
+ return (ENXIO);
+
+ if (sc == NULL || sc->sc_iface == NULL || sc->sc_dying)
+ return (ENXIO);
+
+ if (sc->sc_state)
+ return (EBUSY);
+
+ sc->sc_state = ULPT_INIT;
+ sc->sc_flags = flags;
+ DPRINTF(("ulptopen: flags=0x%x\n", (unsigned)flags));
+
+#if defined(USB_DEBUG)
+ /* Ignoring these flags might not be a good idea */
+ if ((flags & ~ULPT_NOPRIME) != 0)
+ printf("ulptopen: flags ignored: %b\n", flags,
+ "\20\3POS_INIT\4POS_ACK\6PRIME_OPEN\7AUTOLF\10BYPASS");
+#endif
+
+
+ error = 0;
+ sc->sc_refcnt++;
+
+ if ((flags & ULPT_NOPRIME) == 0) {
+ ulpt_reset(sc);
+ if (sc->sc_dying) {
+ error = ENXIO;
+ sc->sc_state = 0;
+ goto done;
+ }
+ }
+
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe);
+ if (err) {
+ error = EIO;
+ goto err0;
+ }
+ sc->sc_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_out_xfer == NULL) {
+ error = ENOMEM;
+ goto err1;
+ }
+ sc->sc_out_buf = usbd_alloc_buffer(sc->sc_out_xfer, ULPT_BSIZE);
+ if (sc->sc_out_buf == NULL) {
+ error = ENOMEM;
+ goto err2;
+ }
+
+ if (ulptusein && sc->sc_in != -1) {
+ DPRINTF(("ulpt_open: open input pipe\n"));
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_in,0,&sc->sc_in_pipe);
+ if (err) {
+ error = EIO;
+ goto err2;
+ }
+ sc->sc_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_in_xfer == NULL) {
+ error = ENOMEM;
+ goto err3;
+ }
+ sc->sc_in_buf = usbd_alloc_buffer(sc->sc_in_xfer, ULPT_BSIZE);
+ if (sc->sc_in_buf == NULL) {
+ error = ENOMEM;
+ goto err4;
+ }
+
+ /* If it's not opened for read the set up a reader. */
+ if (!(flag & FREAD)) {
+ DPRINTF(("ulpt_open: start read callout\n"));
+ callout_init(&sc->sc_read_callout, 0);
+ callout_reset(&sc->sc_read_callout, hz/5, ulpt_tick,
+ sc);
+ sc->sc_has_callout = 1;
+ }
+ }
+
+ sc->sc_state = ULPT_OPEN;
+ goto done;
+
+ err4:
+ usbd_free_xfer(sc->sc_in_xfer);
+ sc->sc_in_xfer = NULL;
+ err3:
+ usbd_close_pipe(sc->sc_in_pipe);
+ sc->sc_in_pipe = NULL;
+ err2:
+ usbd_free_xfer(sc->sc_out_xfer);
+ sc->sc_out_xfer = NULL;
+ err1:
+ usbd_close_pipe(sc->sc_out_pipe);
+ sc->sc_out_pipe = NULL;
+ err0:
+ sc->sc_state = 0;
+
+ done:
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+
+ DPRINTF(("ulptopen: done, error=%d\n", error));
+ return (error);
+}
+
+int
+ulpt_statusmsg(u_char status, struct ulpt_softc *sc)
+{
+ u_char new;
+
+ status = (status ^ LPS_INVERT) & LPS_MASK;
+ new = status & ~sc->sc_laststatus;
+ sc->sc_laststatus = status;
+
+ if (new & LPS_SELECT)
+ log(LOG_NOTICE, "%s: offline\n", device_get_nameunit(sc->sc_dev));
+ else if (new & LPS_NOPAPER)
+ log(LOG_NOTICE, "%s: out of paper\n", device_get_nameunit(sc->sc_dev));
+ else if (new & LPS_NERR)
+ log(LOG_NOTICE, "%s: output error\n", device_get_nameunit(sc->sc_dev));
+
+ return (status);
+}
+
+int
+ulptclose(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct ulpt_softc *sc;
+
+ sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev));
+
+ if (sc->sc_state != ULPT_OPEN)
+ /* We are being forced to close before the open completed. */
+ return (0);
+
+ if (sc->sc_has_callout) {
+ callout_stop(&sc->sc_read_callout);
+ sc->sc_has_callout = 0;
+ }
+
+ if (sc->sc_out_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_out_pipe);
+ usbd_close_pipe(sc->sc_out_pipe);
+ sc->sc_out_pipe = NULL;
+ }
+ if (sc->sc_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_out_xfer);
+ sc->sc_out_xfer = NULL;
+ }
+
+ if (sc->sc_in_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_in_pipe);
+ usbd_close_pipe(sc->sc_in_pipe);
+ sc->sc_in_pipe = NULL;
+ }
+ if (sc->sc_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_in_xfer);
+ sc->sc_in_xfer = NULL;
+ }
+
+ sc->sc_state = 0;
+
+ DPRINTF(("ulptclose: closed\n"));
+ return (0);
+}
+
+int
+ulpt_do_write(struct ulpt_softc *sc, struct uio *uio, int flags)
+{
+ u_int32_t n;
+ int error = 0;
+ void *bufp;
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ DPRINTF(("ulptwrite\n"));
+ xfer = sc->sc_out_xfer;
+ bufp = sc->sc_out_buf;
+ while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) {
+ ulpt_statusmsg(ulpt_status(sc), sc);
+ error = uiomove(bufp, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n));
+ err = usbd_bulk_transfer(xfer, sc->sc_out_pipe, USBD_NO_COPY,
+ USBD_NO_TIMEOUT, bufp, &n, "ulptwr");
+ if (err) {
+ DPRINTF(("ulptwrite: error=%d\n", err));
+ error = EIO;
+ break;
+ }
+ }
+
+ return (error);
+}
+
+int
+ulptwrite(struct cdev *dev, struct uio *uio, int flags)
+{
+ struct ulpt_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ sc->sc_refcnt++;
+ error = ulpt_do_write(sc, uio, flags);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ return (error);
+}
+
+int
+ulpt_do_read(struct ulpt_softc *sc, struct uio *uio, int flags)
+{
+ u_int32_t n, on;
+ int error = 0;
+ void *bufp;
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ DPRINTF(("ulptread\n"));
+
+ if (sc->sc_in_pipe == NULL)
+ return 0;
+
+ xfer = sc->sc_in_xfer;
+ bufp = sc->sc_in_buf;
+ while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("ulptread: transfer %d bytes\n", n));
+ on = n;
+ err = usbd_bulk_transfer(xfer, sc->sc_in_pipe,
+ USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT, bufp, &n, "ulptrd");
+ if (err) {
+ DPRINTF(("ulptread: error=%d\n", err));
+ error = EIO;
+ break;
+ }
+ error = uiomove(bufp, n, uio);
+ if (error)
+ break;
+ if (on != n)
+ break;
+ }
+
+ return (error);
+}
+
+int
+ulptread(struct cdev *dev, struct uio *uio, int flags)
+{
+ struct ulpt_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(ulpt_devclass, ULPTUNIT(dev));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ sc->sc_refcnt++;
+ error = ulpt_do_read(sc, uio, flags);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ return (error);
+}
+
+void
+ulpt_read_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ usbd_status err;
+ u_int32_t n;
+ usbd_private_handle xsc;
+ struct ulpt_softc *sc;
+
+ usbd_get_xfer_status(xfer, &xsc, NULL, &n, &err);
+ sc = xsc;
+
+ DPRINTFN(1,("ulpt_read_cb: start sc=%p, err=%d n=%d\n", sc, err, n));
+
+#ifdef ULPT_DEBUG
+ if (!err && n > 0)
+ DPRINTF(("ulpt_tick: discarding %d bytes\n", n));
+#endif
+ if (!err || err == USBD_TIMEOUT)
+ callout_reset(&sc->sc_read_callout, hz / ULPT_READS_PER_SEC,
+ ulpt_tick, sc);
+}
+
+void
+ulpt_tick(void *xsc)
+{
+ struct ulpt_softc *sc = xsc;
+ usbd_status err;
+
+ if (sc == NULL || sc->sc_dying)
+ return;
+
+ DPRINTFN(1,("ulpt_tick: start sc=%p\n", sc));
+
+ usbd_setup_xfer(sc->sc_in_xfer, sc->sc_in_pipe, sc, sc->sc_in_buf,
+ ULPT_BSIZE, USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ ULPT_READ_TIMO, ulpt_read_cb);
+ err = usbd_transfer(sc->sc_in_xfer);
+ DPRINTFN(1,("ulpt_tick: err=%d\n", err));
+}
+
+int
+ulptioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *p)
+{
+ int error = 0;
+
+ switch (cmd) {
+ default:
+ error = ENODEV;
+ }
+
+ return (error);
+}
+
+#if 0
+/* XXX This does not belong here. */
+/*
+ * Print select parts of an IEEE 1284 device ID.
+ */
+void
+ieee1284_print_id(char *str)
+{
+ char *p, *q;
+
+ for (p = str-1; p; p = strchr(p, ';')) {
+ p++; /* skip ';' */
+ if (strncmp(p, "MFG:", 4) == 0 ||
+ strncmp(p, "MANUFACTURER:", 14) == 0 ||
+ strncmp(p, "MDL:", 4) == 0 ||
+ strncmp(p, "MODEL:", 6) == 0) {
+ q = strchr(p, ';');
+ if (q)
+ printf("%.*s", (int)(q - p + 1), p);
+ }
+ }
+}
+#endif
diff --git a/sys/legacy/dev/usb/umass.c b/sys/legacy/dev/usb/umass.c
new file mode 100644
index 0000000..b0b73cc
--- /dev/null
+++ b/sys/legacy/dev/usb/umass.c
@@ -0,0 +1,3611 @@
+/*-
+ * Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>,
+ * Nick Hibma <n_hibma@freebsd.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ * $NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $
+ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: umass.c,v 1.67 2001/11/25 19:05:22 augustss Exp $
+ * $NetBSD: umass.c,v 1.90 2002/11/04 19:17:33 pooka Exp $
+ * $NetBSD: umass.c,v 1.108 2003/11/07 17:03:25 wiz Exp $
+ * $NetBSD: umass.c,v 1.109 2003/12/04 13:57:31 keihan Exp $
+ */
+
+/*
+ * Universal Serial Bus Mass Storage Class specs:
+ * http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf
+ * http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
+ * http://www.usb.org/developers/devclass_docs/usb_msc_cbi_1.1.pdf
+ * http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf
+ */
+
+/*
+ * Ported to NetBSD by Lennart Augustsson <augustss@NetBSD.org>.
+ * Parts of the code written by Jason R. Thorpe <thorpej@shagadelic.org>.
+ */
+
+/*
+ * The driver handles 3 Wire Protocols
+ * - Command/Bulk/Interrupt (CBI)
+ * - Command/Bulk/Interrupt with Command Completion Interrupt (CBI with CCI)
+ * - Mass Storage Bulk-Only (BBB)
+ * (BBB refers Bulk/Bulk/Bulk for Command/Data/Status phases)
+ *
+ * Over these wire protocols it handles the following command protocols
+ * - SCSI
+ * - UFI (floppy command set)
+ * - 8070i (ATAPI)
+ *
+ * UFI and 8070i (ATAPI) are transformed versions of the SCSI command set. The
+ * sc->transform method is used to convert the commands into the appropriate
+ * format (if at all necessary). For example, UFI requires all commands to be
+ * 12 bytes in length amongst other things.
+ *
+ * The source code below is marked and can be split into a number of pieces
+ * (in this order):
+ *
+ * - probe/attach/detach
+ * - generic transfer routines
+ * - BBB
+ * - CBI
+ * - CBI_I (in addition to functions from CBI)
+ * - CAM (Common Access Method)
+ * - SCSI
+ * - UFI
+ * - 8070i (ATAPI)
+ *
+ * The protocols are implemented using a state machine, for the transfers as
+ * well as for the resets. The state machine is contained in umass_*_state.
+ * The state machine is started through either umass_*_transfer or
+ * umass_*_reset.
+ *
+ * The reason for doing this is a) CAM performs a lot better this way and b) it
+ * avoids using tsleep from interrupt context (for example after a failed
+ * transfer).
+ */
+
+/*
+ * The SCSI related part of this driver has been derived from the
+ * dev/ppbus/vpo.c driver, by Nicolas Souchu (nsouch@freebsd.org).
+ *
+ * The CAM layer uses so called actions which are messages sent to the host
+ * adapter for completion. The actions come in through umass_cam_action. The
+ * appropriate block of routines is called depending on the transport protocol
+ * in use. When the transfer has finished, these routines call
+ * umass_cam_cb again to complete the CAM command.
+ */
+
+/*
+ * XXX Currently CBI with CCI is not supported because it bombs the system
+ * when the device is detached (low frequency interrupts are detached
+ * too late.
+ */
+#undef CBI_I
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_da.h>
+
+#include <cam/cam_periph.h>
+
+#ifdef USB_DEBUG
+#define DIF(m, x) if (umassdebug & (m)) do { x ; } while (0)
+#define DPRINTF(m, x) if (umassdebug & (m)) printf x
+#define UDMASS_GEN 0x00010000 /* general */
+#define UDMASS_SCSI 0x00020000 /* scsi */
+#define UDMASS_UFI 0x00040000 /* ufi command set */
+#define UDMASS_ATAPI 0x00080000 /* 8070i command set */
+#define UDMASS_CMD (UDMASS_SCSI|UDMASS_UFI|UDMASS_ATAPI)
+#define UDMASS_USB 0x00100000 /* USB general */
+#define UDMASS_BBB 0x00200000 /* Bulk-Only transfers */
+#define UDMASS_CBI 0x00400000 /* CBI transfers */
+#define UDMASS_WIRE (UDMASS_BBB|UDMASS_CBI)
+#define UDMASS_ALL 0xffff0000 /* all of the above */
+int umassdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass");
+SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RW,
+ &umassdebug, 0, "umass debug level");
+#else
+#define DIF(m, x) /* nop */
+#define DPRINTF(m, x) /* nop */
+#endif
+
+
+/* Generic definitions */
+
+/* Direction for umass_*_transfer */
+#define DIR_NONE 0
+#define DIR_IN 1
+#define DIR_OUT 2
+
+/* device name */
+#define DEVNAME "umass"
+#define DEVNAME_SIM "umass-sim"
+
+#define UMASS_MAX_TRANSFER_SIZE 65536
+/* Approximate maximum transfer speeds (assumes 33% overhead). */
+#define UMASS_FULL_TRANSFER_SPEED 1000
+#define UMASS_HIGH_TRANSFER_SPEED 40000
+#define UMASS_FLOPPY_TRANSFER_SPEED 20
+
+#define UMASS_TIMEOUT 5000 /* msecs */
+
+/* CAM specific definitions */
+
+#define UMASS_SCSIID_MAX 1 /* maximum number of drives expected */
+#define UMASS_SCSIID_HOST UMASS_SCSIID_MAX
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+
+/* Bulk-Only features */
+
+#define UR_BBB_RESET 0xff /* Bulk-Only reset */
+#define UR_BBB_GET_MAX_LUN 0xfe /* Get maximum lun */
+
+/* Command Block Wrapper */
+typedef struct {
+ uDWord dCBWSignature;
+# define CBWSIGNATURE 0x43425355
+ uDWord dCBWTag;
+ uDWord dCBWDataTransferLength;
+ uByte bCBWFlags;
+# define CBWFLAGS_OUT 0x00
+# define CBWFLAGS_IN 0x80
+ uByte bCBWLUN;
+ uByte bCDBLength;
+# define CBWCDBLENGTH 16
+ uByte CBWCDB[CBWCDBLENGTH];
+} __packed umass_bbb_cbw_t;
+#define UMASS_BBB_CBW_SIZE 31
+
+/* Command Status Wrapper */
+typedef struct {
+ uDWord dCSWSignature;
+# define CSWSIGNATURE 0x53425355
+# define CSWSIGNATURE_IMAGINATION_DBX1 0x43425355
+# define CSWSIGNATURE_OLYMPUS_C1 0x55425355
+ uDWord dCSWTag;
+ uDWord dCSWDataResidue;
+ uByte bCSWStatus;
+# define CSWSTATUS_GOOD 0x0
+# define CSWSTATUS_FAILED 0x1
+# define CSWSTATUS_PHASE 0x2
+} __packed umass_bbb_csw_t;
+#define UMASS_BBB_CSW_SIZE 13
+
+/* CBI features */
+
+#define UR_CBI_ADSC 0x00
+
+typedef unsigned char umass_cbi_cbl_t[16]; /* Command block */
+
+typedef union {
+ struct {
+ unsigned char type;
+ #define IDB_TYPE_CCI 0x00
+ unsigned char value;
+ #define IDB_VALUE_PASS 0x00
+ #define IDB_VALUE_FAIL 0x01
+ #define IDB_VALUE_PHASE 0x02
+ #define IDB_VALUE_PERSISTENT 0x03
+ #define IDB_VALUE_STATUS_MASK 0x03
+ } common;
+
+ struct {
+ unsigned char asc;
+ unsigned char ascq;
+ } ufi;
+} umass_cbi_sbl_t;
+
+
+
+struct umass_softc; /* see below */
+
+typedef void (*transfer_cb_f) (struct umass_softc *sc, void *priv,
+ int residue, int status);
+#define STATUS_CMD_OK 0 /* everything ok */
+#define STATUS_CMD_UNKNOWN 1 /* will have to fetch sense */
+#define STATUS_CMD_FAILED 2 /* transfer was ok, command failed */
+#define STATUS_WIRE_FAILED 3 /* couldn't even get command across */
+
+typedef void (*wire_reset_f) (struct umass_softc *sc, int status);
+typedef void (*wire_transfer_f) (struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen, void *data, int datalen,
+ int dir, u_int timeout, transfer_cb_f cb, void *priv);
+typedef void (*wire_state_f) (usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status err);
+
+typedef int (*command_transform_f) (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+
+struct umass_devdescr_t {
+ u_int32_t vid;
+# define VID_WILDCARD 0xffffffff
+# define VID_EOT 0xfffffffe
+ u_int32_t pid;
+# define PID_WILDCARD 0xffffffff
+# define PID_EOT 0xfffffffe
+ u_int32_t rid;
+# define RID_WILDCARD 0xffffffff
+# define RID_EOT 0xfffffffe
+
+ /* wire and command protocol */
+ u_int16_t proto;
+# define UMASS_PROTO_BBB 0x0001 /* USB wire protocol */
+# define UMASS_PROTO_CBI 0x0002
+# define UMASS_PROTO_CBI_I 0x0004
+# define UMASS_PROTO_WIRE 0x00ff /* USB wire protocol mask */
+# define UMASS_PROTO_SCSI 0x0100 /* command protocol */
+# define UMASS_PROTO_ATAPI 0x0200
+# define UMASS_PROTO_UFI 0x0400
+# define UMASS_PROTO_RBC 0x0800
+# define UMASS_PROTO_COMMAND 0xff00 /* command protocol mask */
+
+ /* Device specific quirks */
+ u_int16_t quirks;
+# define NO_QUIRKS 0x0000
+ /* The drive does not support Test Unit Ready. Convert to Start Unit
+ */
+# define NO_TEST_UNIT_READY 0x0001
+ /* The drive does not reset the Unit Attention state after REQUEST
+ * SENSE has been sent. The INQUIRY command does not reset the UA
+ * either, and so CAM runs in circles trying to retrieve the initial
+ * INQUIRY data.
+ */
+# define RS_NO_CLEAR_UA 0x0002
+ /* The drive does not support START STOP. */
+# define NO_START_STOP 0x0004
+ /* Don't ask for full inquiry data (255b). */
+# define FORCE_SHORT_INQUIRY 0x0008
+ /* Needs to be initialised the Shuttle way */
+# define SHUTTLE_INIT 0x0010
+ /* Drive needs to be switched to alternate iface 1 */
+# define ALT_IFACE_1 0x0020
+ /* Drive does not do 1Mb/s, but just floppy speeds (20kb/s) */
+# define FLOPPY_SPEED 0x0040
+ /* The device can't count and gets the residue of transfers wrong */
+# define IGNORE_RESIDUE 0x0080
+ /* No GetMaxLun call */
+# define NO_GETMAXLUN 0x0100
+ /* The device uses a weird CSWSIGNATURE. */
+# define WRONG_CSWSIG 0x0200
+ /* Device cannot handle INQUIRY so fake a generic response */
+# define NO_INQUIRY 0x0400
+ /* Device cannot handle INQUIRY EVPD, return CHECK CONDITION */
+# define NO_INQUIRY_EVPD 0x0800
+ /* Pad all RBC requests to 12 bytes. */
+# define RBC_PAD_TO_12 0x1000
+ /* Device reports number of sectors from READ_CAPACITY, not max
+ * sector number.
+ */
+# define READ_CAPACITY_OFFBY1 0x2000
+ /* Device cannot handle a SCSI synchronize cache command. Normally
+ * this quirk would be handled in the cam layer, but for IDE bridges
+ * we need to associate the quirk with the bridge and not the
+ * underlying disk device. This is handled by faking a success result.
+ */
+# define NO_SYNCHRONIZE_CACHE 0x4000
+};
+
+static struct umass_devdescr_t umass_devdescrs[] = {
+ { USB_VENDOR_ADDONICS2, USB_PRODUCT_ADDONICS2_CABLE_205, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_AIPTEK, USB_PRODUCT_AIPTEK_POCKETCAM3M, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_UMCR_9361, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO230, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO330, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_ASAHIOPTICAL, PID_WILDCARD, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ RS_NO_CLEAR_UA
+ },
+ { USB_VENDOR_ADDON, USB_PRODUCT_ADDON_ATTACHE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_ADDON, USB_PRODUCT_ADDON_A256MB, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_ADDON, USB_PRODUCT_ADDON_DISKPRO512, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2SCSI, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_CASIO, USB_PRODUCT_CASIO_QV_DIGICAM, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_CCYU, USB_PRODUCT_CCYU_ED1064, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_CENTURY, USB_PRODUCT_CENTURY_EX35QUAT, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_DESKNOTE, USB_PRODUCT_DESKNOTE_UCR_61S2B, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_DMI, USB_PRODUCT_DMI_CFSM_RW, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_STYLUS_875DC, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_EPSON, USB_PRODUCT_EPSON_STYLUS_895, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_FEIYA, USB_PRODUCT_FEIYA_5IN1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_FREECOM, USB_PRODUCT_FREECOM_DVD, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_FUJIPHOTO, USB_PRODUCT_FUJIPHOTO_MASS0100, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ RS_NO_CLEAR_UA
+ },
+ { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE_2, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB_2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ WRONG_CSWSIG
+ },
+ { USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FG, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FGSM, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_USB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_DZ_MV100A, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_HP, USB_PRODUCT_HP_CDW4E, RID_WILDCARD,
+ UMASS_PROTO_ATAPI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_HP, USB_PRODUCT_HP_CDW8200, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_TEST_UNIT_READY | NO_START_STOP
+ },
+ { USB_VENDOR_IMAGINATION, USB_PRODUCT_IMAGINATION_DBX1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ WRONG_CSWSIG
+ },
+ { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ATAPI, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_STORAGE_V2, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_USBCABLE, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_TEST_UNIT_READY | NO_START_STOP | ALT_IFACE_1
+ },
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_IU_CD2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_DVR_UEH8, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP100, RID_WILDCARD,
+ /* XXX This is not correct as there are Zip drives that use ATAPI.
+ */
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_TEST_UNIT_READY
+ },
+ { USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_L3, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S3X, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S4, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S5, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_LACIE, USB_PRODUCT_LACIE_HD, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_CF_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_JUMPSHOT, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443SU2, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443U2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MELCO, USB_PRODUCT_MELCO_DUBPXXG, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_DPCM, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_TEST_UNIT_READY | NO_START_STOP
+ },
+ { USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIDB25, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIHD50, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_E223, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_F300, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_CDRRW, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_FDD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_E398, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_INQUIRY_EVPD | NO_GETMAXLUN
+ },
+ { USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE | NO_GETMAXLUN | RS_NO_CLEAR_UA
+ },
+ { USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY2, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_MYSON, USB_PRODUCT_MYSON_HEDEN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND3260, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY
+ },
+ { USB_VENDOR_NETAC, USB_PRODUCT_NETAC_CF_CARD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_NETAC, USB_PRODUCT_NETAC_ONLYDISK, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_CLIK_40, RID_WILDCARD,
+ UMASS_PROTO_ATAPI,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_NIKON, USB_PRODUCT_NIKON_D300, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ WRONG_CSWSIG
+ },
+ { USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C700, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFMS_RW, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_COMBO, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_READER2, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_SDS_HOTFIND_D, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN | NO_SYNCHRONIZE_CACHE
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MDCFE_B_CF_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MDSM_B_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_UCF100, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_INQUIRY | NO_GETMAXLUN
+ },
+ { USB_VENDOR_ONSPEC2, USB_PRODUCT_ONSPEC2_IMAGEMATE_SDDR55, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXL840AN, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB20AN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB35AN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_LS120CAM, RID_WILDCARD,
+ UMASS_PROTO_UFI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_PLEXTOR, USB_PRODUCT_PLEXTOR_40_12_40U, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_TEST_UNIT_READY
+ },
+ { USB_VENDOR_PNY, USB_PRODUCT_PNY_ATTACHE2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE | NO_START_STOP
+ },
+ { USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_YP_U2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ SHUTTLE_INIT | NO_GETMAXLUN
+ },
+ { USB_VENDOR_SAMSUNG_TECHWIN, USB_PRODUCT_SAMSUNG_TECHWIN_DIGIMAX_410, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR05A, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ READ_CAPACITY_OFFBY1 | NO_GETMAXLUN
+ },
+ { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR09, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ READ_CAPACITY_OFFBY1 | NO_GETMAXLUN
+ },
+ { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR12, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ READ_CAPACITY_OFFBY1 | NO_GETMAXLUN
+ },
+ { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR31, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ READ_CAPACITY_OFFBY1
+ },
+ { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ2_256, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_128, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_256, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CDRW, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CF, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_TEST_UNIT_READY | NO_START_STOP | SHUTTLE_INIT
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBATAPI, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBCFSM, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_HIFD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SDDR09, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_ZIOMMC, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_I_BEAD100, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ SHUTTLE_INIT
+ },
+ { USB_VENDOR_SIIG, USB_PRODUCT_SIIG_WINTERREADER, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_SKANHEX, USB_PRODUCT_SKANHEX_MD_7425, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_SKANHEX, USB_PRODUCT_SKANHEX_SX_520Z, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40_MS, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, 0x0500,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ RBC_PAD_TO_12
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, 0x0600,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ RBC_PAD_TO_12
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_HANDYCAM, 0x0500,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ RBC_PAD_TO_12
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_HANDYCAM, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_MSC_U03, RID_WILDCARD,
+ UMASS_PROTO_UFI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_NW_MS7, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_PEG_N760C, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_MSACUS1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_PORTABLE_HDD_V2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_TAUGA, USB_PRODUCT_TAUGA_CAMERAMATE, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_TEAC, USB_PRODUCT_TEAC_FD05PUB, RID_WILDCARD,
+ UMASS_PROTO_UFI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_TREK, USB_PRODUCT_TREK_MEMKEY, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE_8MB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ { USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_C3310, RID_WILDCARD,
+ UMASS_PROTO_UFI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_MP3, RID_WILDCARD,
+ UMASS_PROTO_RBC,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_T33520, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_MDIV, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ { USB_VENDOR_VIA, USB_PRODUCT_VIA_USB2IDEBRIDGE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_SYNCHRONIZE_CACHE
+ },
+ { USB_VENDOR_VIVITAR, USB_PRODUCT_VIVITAR_35XX, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_COMBO, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_EXTHDD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_MYBOOK, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY_EVPD
+ },
+ { USB_VENDOR_WINMAXGROUP, USB_PRODUCT_WINMAXGROUP_FLASH64MC, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ { USB_VENDOR_YANO, USB_PRODUCT_YANO_FW800HD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ { USB_VENDOR_YANO, USB_PRODUCT_YANO_U640MO, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ FORCE_SHORT_INQUIRY
+ },
+ { USB_VENDOR_YEDATA, USB_PRODUCT_YEDATA_FLASHBUSTERU, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ { USB_VENDOR_ZORAN, USB_PRODUCT_ZORAN_EX20DSC, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ { VID_EOT, PID_EOT, RID_EOT, 0, 0 }
+};
+
+
+/* the per device structure */
+struct umass_softc {
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_udev; /* USB device */
+
+ struct cam_sim *umass_sim; /* SCSI Interface Module */
+
+ unsigned char flags; /* various device flags */
+# define UMASS_FLAGS_GONE 0x01 /* devices is no more */
+
+ u_int16_t proto; /* wire and cmd protocol */
+ u_int16_t quirks; /* they got it almost right */
+
+ usbd_interface_handle iface; /* Mass Storage interface */
+ int ifaceno; /* MS iface number */
+
+ u_int8_t bulkin; /* bulk-in Endpoint Address */
+ u_int8_t bulkout; /* bulk-out Endpoint Address */
+ u_int8_t intrin; /* intr-in Endp. (CBI) */
+ usbd_pipe_handle bulkin_pipe;
+ usbd_pipe_handle bulkout_pipe;
+ usbd_pipe_handle intrin_pipe;
+
+ /* Reset the device in a wire protocol specific way */
+ wire_reset_f reset;
+
+ /* The start of a wire transfer. It prepares the whole transfer (cmd,
+ * data, and status stage) and initiates it. It is up to the state
+ * machine (below) to handle the various stages and errors in these
+ */
+ wire_transfer_f transfer;
+
+ /* The state machine, handling the various states during a transfer */
+ wire_state_f state;
+
+ /* The command transform function is used to conver the SCSI commands
+ * into their derivatives, like UFI, ATAPI, and friends.
+ */
+ command_transform_f transform; /* command transform */
+
+ /* Bulk specific variables for transfers in progress */
+ umass_bbb_cbw_t cbw; /* command block wrapper */
+ umass_bbb_csw_t csw; /* command status wrapper*/
+ /* CBI specific variables for transfers in progress */
+ umass_cbi_cbl_t cbl; /* command block */
+ umass_cbi_sbl_t sbl; /* status block */
+
+ /* generic variables for transfers in progress */
+ /* ctrl transfer requests */
+ usb_device_request_t request;
+
+ /* xfer handles
+ * Most of our operations are initiated from interrupt context, so
+ * we need to avoid using the one that is in use. We want to avoid
+ * allocating them in the interrupt context as well.
+ */
+ /* indices into array below */
+# define XFER_BBB_CBW 0 /* Bulk-Only */
+# define XFER_BBB_DATA 1
+# define XFER_BBB_DCLEAR 2
+# define XFER_BBB_CSW1 3
+# define XFER_BBB_CSW2 4
+# define XFER_BBB_SCLEAR 5
+# define XFER_BBB_RESET1 6
+# define XFER_BBB_RESET2 7
+# define XFER_BBB_RESET3 8
+
+# define XFER_CBI_CB 0 /* CBI */
+# define XFER_CBI_DATA 1
+# define XFER_CBI_STATUS 2
+# define XFER_CBI_DCLEAR 3
+# define XFER_CBI_SCLEAR 4
+# define XFER_CBI_RESET1 5
+# define XFER_CBI_RESET2 6
+# define XFER_CBI_RESET3 7
+
+# define XFER_NR 9 /* maximum number */
+
+ usbd_xfer_handle transfer_xfer[XFER_NR]; /* for ctrl xfers */
+
+ int transfer_dir; /* data direction */
+ void *transfer_data; /* data buffer */
+ int transfer_datalen; /* (maximum) length */
+ int transfer_actlen; /* actual length */
+ transfer_cb_f transfer_cb; /* callback */
+ void *transfer_priv; /* for callback */
+ int transfer_status;
+
+ int transfer_state;
+# define TSTATE_ATTACH 0 /* in attach */
+# define TSTATE_IDLE 1
+# define TSTATE_BBB_COMMAND 2 /* CBW transfer */
+# define TSTATE_BBB_DATA 3 /* Data transfer */
+# define TSTATE_BBB_DCLEAR 4 /* clear endpt stall */
+# define TSTATE_BBB_STATUS1 5 /* clear endpt stall */
+# define TSTATE_BBB_SCLEAR 6 /* clear endpt stall */
+# define TSTATE_BBB_STATUS2 7 /* CSW transfer */
+# define TSTATE_BBB_RESET1 8 /* reset command */
+# define TSTATE_BBB_RESET2 9 /* in clear stall */
+# define TSTATE_BBB_RESET3 10 /* out clear stall */
+# define TSTATE_CBI_COMMAND 11 /* command transfer */
+# define TSTATE_CBI_DATA 12 /* data transfer */
+# define TSTATE_CBI_STATUS 13 /* status transfer */
+# define TSTATE_CBI_DCLEAR 14 /* clear ep stall */
+# define TSTATE_CBI_SCLEAR 15 /* clear ep stall */
+# define TSTATE_CBI_RESET1 16 /* reset command */
+# define TSTATE_CBI_RESET2 17 /* in clear stall */
+# define TSTATE_CBI_RESET3 18 /* out clear stall */
+# define TSTATE_STATES 19 /* # of states above */
+
+
+ /* SCSI/CAM specific variables */
+ unsigned char cam_scsi_command[CAM_MAX_CDBLEN];
+ unsigned char cam_scsi_command2[CAM_MAX_CDBLEN];
+ struct scsi_sense cam_scsi_sense;
+ struct scsi_sense cam_scsi_test_unit_ready;
+ struct callout cam_scsi_rescan_ch;
+
+ int timeout; /* in msecs */
+
+ int maxlun; /* maximum LUN number */
+};
+
+#ifdef USB_DEBUG
+char *states[TSTATE_STATES+1] = {
+ /* should be kept in sync with the list at transfer_state */
+ "Attach",
+ "Idle",
+ "BBB CBW",
+ "BBB Data",
+ "BBB Data bulk-in/-out clear stall",
+ "BBB CSW, 1st attempt",
+ "BBB CSW bulk-in clear stall",
+ "BBB CSW, 2nd attempt",
+ "BBB Reset",
+ "BBB bulk-in clear stall",
+ "BBB bulk-out clear stall",
+ "CBI Command",
+ "CBI Data",
+ "CBI Status",
+ "CBI Data bulk-in/-out clear stall",
+ "CBI Status intr-in clear stall",
+ "CBI Reset",
+ "CBI bulk-in clear stall",
+ "CBI bulk-out clear stall",
+ NULL
+};
+#endif
+
+/* If device cannot return valid inquiry data, fake it */
+static uint8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = {
+ 0, /*removable*/ 0x80, SCSI_REV_2, SCSI_REV_2,
+ /*additional_length*/ 31, 0, 0, 0
+};
+
+/* USB device probe/attach/detach functions */
+static device_probe_t umass_match;
+static device_attach_t umass_attach;
+static device_detach_t umass_detach;
+
+static device_method_t umass_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, umass_match),
+ DEVMETHOD(device_attach, umass_attach),
+ DEVMETHOD(device_detach, umass_detach),
+
+ { 0, 0 }
+};
+
+static driver_t umass_driver = {
+ "umass",
+ umass_methods,
+ sizeof(struct umass_softc)
+};
+
+static devclass_t umass_devclass;
+
+static int umass_match_proto (struct umass_softc *sc,
+ usbd_interface_handle iface,
+ usbd_device_handle udev);
+
+/* quirk functions */
+static void umass_init_shuttle (struct umass_softc *sc);
+
+/* generic transfer functions */
+static usbd_status umass_setup_transfer (struct umass_softc *sc,
+ usbd_pipe_handle pipe,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer);
+static usbd_status umass_setup_ctrl_transfer (struct umass_softc *sc,
+ usbd_device_handle udev,
+ usb_device_request_t *req,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer);
+static void umass_clear_endpoint_stall (struct umass_softc *sc,
+ u_int8_t endpt, usbd_pipe_handle pipe,
+ int state, usbd_xfer_handle xfer);
+static void umass_reset (struct umass_softc *sc,
+ transfer_cb_f cb, void *priv);
+
+/* Bulk-Only related functions */
+static void umass_bbb_reset (struct umass_softc *sc, int status);
+static void umass_bbb_transfer (struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen,
+ void *data, int datalen, int dir, u_int timeout,
+ transfer_cb_f cb, void *priv);
+static void umass_bbb_state (usbd_xfer_handle xfer,
+ usbd_private_handle priv,
+ usbd_status err);
+static int umass_bbb_get_max_lun
+ (struct umass_softc *sc);
+
+/* CBI related functions */
+static int umass_cbi_adsc (struct umass_softc *sc,
+ char *buffer, int buflen,
+ usbd_xfer_handle xfer);
+static void umass_cbi_reset (struct umass_softc *sc, int status);
+static void umass_cbi_transfer (struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen,
+ void *data, int datalen, int dir, u_int timeout,
+ transfer_cb_f cb, void *priv);
+static void umass_cbi_state (usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status err);
+
+/* CAM related functions */
+static void umass_cam_action (struct cam_sim *sim, union ccb *ccb);
+static void umass_cam_poll (struct cam_sim *sim);
+
+static void umass_cam_cb (struct umass_softc *sc, void *priv,
+ int residue, int status);
+static void umass_cam_sense_cb (struct umass_softc *sc, void *priv,
+ int residue, int status);
+static void umass_cam_quirk_cb (struct umass_softc *sc, void *priv,
+ int residue, int status);
+
+static void umass_cam_rescan_callback
+ (struct cam_periph *periph,union ccb *ccb);
+static void umass_cam_rescan (void *addr);
+
+static int umass_cam_attach_sim (struct umass_softc *sc);
+static int umass_cam_attach (struct umass_softc *sc);
+static int umass_cam_detach_sim (struct umass_softc *sc);
+
+
+/* SCSI specific functions */
+static int umass_scsi_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+/* UFI specific functions */
+#define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12 bytes */
+static int umass_ufi_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+/* ATAPI (8070i) specific functions */
+#define ATAPI_COMMAND_LENGTH 12 /* ATAPI commands are always 12 bytes */
+static int umass_atapi_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+/* RBC specific functions */
+static int umass_rbc_transform (struct umass_softc *sc,
+ unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen);
+
+#ifdef USB_DEBUG
+/* General debugging functions */
+static void umass_bbb_dump_cbw (struct umass_softc *sc, umass_bbb_cbw_t *cbw);
+static void umass_bbb_dump_csw (struct umass_softc *sc, umass_bbb_csw_t *csw);
+static void umass_cbi_dump_cmd (struct umass_softc *sc, void *cmd, int cmdlen);
+static void umass_dump_buffer (struct umass_softc *sc, u_int8_t *buffer,
+ int buflen, int printlen);
+#endif
+
+MODULE_DEPEND(umass, cam, 1, 1, 1);
+MODULE_DEPEND(umass, usb, 1, 1, 1);
+
+/*
+ * USB device probe/attach/detach
+ */
+
+/*
+ * Match the device we are seeing with the devices supported. Fill in the
+ * description in the softc accordingly. This function is called from both
+ * probe and attach.
+ */
+
+static int
+umass_match_proto(struct umass_softc *sc, usbd_interface_handle iface,
+ usbd_device_handle udev)
+{
+ usb_device_descriptor_t *dd;
+ usb_interface_descriptor_t *id;
+ int i;
+ int found = 0;
+
+ sc->sc_udev = udev;
+ sc->proto = 0;
+ sc->quirks = 0;
+
+ dd = usbd_get_device_descriptor(udev);
+
+ /* An entry specifically for Y-E Data devices as they don't fit in the
+ * device description table.
+ */
+ if (UGETW(dd->idVendor) == USB_VENDOR_YEDATA
+ && UGETW(dd->idProduct) == USB_PRODUCT_YEDATA_FLASHBUSTERU) {
+
+ /* Revisions < 1.28 do not handle the interrupt endpoint
+ * very well.
+ */
+ if (UGETW(dd->bcdDevice) < 0x128) {
+ sc->proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI;
+ } else {
+ sc->proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI_I;
+ }
+
+ /*
+ * Revisions < 1.28 do not have the TEST UNIT READY command
+ * Revisions == 1.28 have a broken TEST UNIT READY
+ */
+ if (UGETW(dd->bcdDevice) <= 0x128)
+ sc->quirks |= NO_TEST_UNIT_READY;
+
+ sc->quirks |= RS_NO_CLEAR_UA | FLOPPY_SPEED;
+ return(UMATCH_VENDOR_PRODUCT);
+ }
+
+ /* Check the list of supported devices for a match. While looking,
+ * check for wildcarded and fully matched. First match wins.
+ */
+ for (i = 0; umass_devdescrs[i].vid != VID_EOT && !found; i++) {
+ if (umass_devdescrs[i].vid == VID_WILDCARD &&
+ umass_devdescrs[i].pid == PID_WILDCARD &&
+ umass_devdescrs[i].rid == RID_WILDCARD) {
+ printf("umass: ignoring invalid wildcard quirk\n");
+ continue;
+ }
+ if ((umass_devdescrs[i].vid == UGETW(dd->idVendor) ||
+ umass_devdescrs[i].vid == VID_WILDCARD)
+ && (umass_devdescrs[i].pid == UGETW(dd->idProduct) ||
+ umass_devdescrs[i].pid == PID_WILDCARD)) {
+ if (umass_devdescrs[i].rid == RID_WILDCARD) {
+ sc->proto = umass_devdescrs[i].proto;
+ sc->quirks = umass_devdescrs[i].quirks;
+ return (UMATCH_VENDOR_PRODUCT);
+ } else if (umass_devdescrs[i].rid ==
+ UGETW(dd->bcdDevice)) {
+ sc->proto = umass_devdescrs[i].proto;
+ sc->quirks = umass_devdescrs[i].quirks;
+ return (UMATCH_VENDOR_PRODUCT_REV);
+ } /* else RID does not match */
+ }
+ }
+
+ /* Check for a standards compliant device */
+ id = usbd_get_interface_descriptor(iface);
+ if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
+ return(UMATCH_NONE);
+
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_SCSI:
+ sc->proto |= UMASS_PROTO_SCSI;
+ break;
+ case UISUBCLASS_UFI:
+ sc->proto |= UMASS_PROTO_UFI;
+ break;
+ case UISUBCLASS_RBC:
+ sc->proto |= UMASS_PROTO_RBC;
+ break;
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ sc->proto |= UMASS_PROTO_ATAPI;
+ break;
+ default:
+ DPRINTF(UDMASS_GEN, ("%s: Unsupported command protocol %d\n",
+ device_get_nameunit(sc->sc_dev), id->bInterfaceSubClass));
+ return(UMATCH_NONE);
+ }
+
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_CBI:
+ sc->proto |= UMASS_PROTO_CBI;
+ break;
+ case UIPROTO_MASS_CBI_I:
+ sc->proto |= UMASS_PROTO_CBI_I;
+ break;
+ case UIPROTO_MASS_BBB_OLD:
+ case UIPROTO_MASS_BBB:
+ sc->proto |= UMASS_PROTO_BBB;
+ break;
+ default:
+ DPRINTF(UDMASS_GEN, ("%s: Unsupported wire protocol %d\n",
+ device_get_nameunit(sc->sc_dev), id->bInterfaceProtocol));
+ return(UMATCH_NONE);
+ }
+
+ return(UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+}
+
+static int
+umass_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct umass_softc *sc = device_get_softc(self);
+
+ sc->sc_dev = self;
+ if (uaa->iface == NULL)
+ return(UMATCH_NONE);
+
+ return(umass_match_proto(sc, uaa->iface, uaa->device));
+}
+
+static int
+umass_attach(device_t self)
+{
+ struct umass_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+ int err;
+
+ /*
+ * the softc struct is bzero-ed in device_set_driver. We can safely
+ * call umass_detach without specifically initialising the struct.
+ */
+ sc->sc_dev = self;
+ sc->iface = uaa->iface;
+ sc->ifaceno = uaa->ifaceno;
+ callout_init(&sc->cam_scsi_rescan_ch, 0);
+
+ /* initialise the proto and drive values in the umass_softc (again) */
+ (void) umass_match_proto(sc, sc->iface, uaa->device);
+
+ id = usbd_get_interface_descriptor(sc->iface);
+#ifdef USB_DEBUG
+ printf("%s: ", device_get_nameunit(sc->sc_dev));
+ switch (sc->proto&UMASS_PROTO_COMMAND) {
+ case UMASS_PROTO_SCSI:
+ printf("SCSI");
+ break;
+ case UMASS_PROTO_ATAPI:
+ printf("8070i (ATAPI)");
+ break;
+ case UMASS_PROTO_UFI:
+ printf("UFI");
+ break;
+ case UMASS_PROTO_RBC:
+ printf("RBC");
+ break;
+ default:
+ printf("(unknown 0x%02x)", sc->proto&UMASS_PROTO_COMMAND);
+ break;
+ }
+ printf(" over ");
+ switch (sc->proto&UMASS_PROTO_WIRE) {
+ case UMASS_PROTO_BBB:
+ printf("Bulk-Only");
+ break;
+ case UMASS_PROTO_CBI: /* uses Comand/Bulk pipes */
+ printf("CBI");
+ break;
+ case UMASS_PROTO_CBI_I: /* uses Comand/Bulk/Interrupt pipes */
+ printf("CBI with CCI");
+#ifndef CBI_I
+ printf(" (using CBI)");
+#endif
+ break;
+ default:
+ printf("(unknown 0x%02x)", sc->proto&UMASS_PROTO_WIRE);
+ }
+ printf("; quirks = 0x%04x\n", sc->quirks);
+#endif
+
+#ifndef CBI_I
+ if (sc->proto & UMASS_PROTO_CBI_I) {
+ /* See beginning of file for comment on the use of CBI with CCI */
+ sc->proto = (sc->proto & ~UMASS_PROTO_CBI_I) | UMASS_PROTO_CBI;
+ }
+#endif
+
+ if (sc->quirks & ALT_IFACE_1) {
+ err = usbd_set_interface(uaa->iface, 1);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: could not switch to "
+ "Alt Interface %d\n",
+ device_get_nameunit(sc->sc_dev), 1));
+ umass_detach(self);
+ return ENXIO;
+ }
+ }
+
+ /*
+ * In addition to the Control endpoint the following endpoints
+ * are required:
+ * a) bulk-in endpoint.
+ * b) bulk-out endpoint.
+ * and for Control/Bulk/Interrupt with CCI (CBI_I)
+ * c) intr-in
+ *
+ * The endpoint addresses are not fixed, so we have to read them
+ * from the device descriptors of the current interface.
+ */
+ for (i = 0 ; i < id->bNumEndpoints ; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->iface, i);
+ if (!ed) {
+ printf("%s: could not read endpoint descriptor\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkin = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkout = ed->bEndpointAddress;
+ } else if (sc->proto & UMASS_PROTO_CBI_I
+ && UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
+ sc->intrin = ed->bEndpointAddress;
+#ifdef USB_DEBUG
+ if (UGETW(ed->wMaxPacketSize) > 2) {
+ DPRINTF(UDMASS_CBI, ("%s: intr size is %d\n",
+ device_get_nameunit(sc->sc_dev),
+ UGETW(ed->wMaxPacketSize)));
+ }
+#endif
+ }
+ }
+
+ /* check whether we found all the endpoints we need */
+ if (!sc->bulkin || !sc->bulkout
+ || (sc->proto & UMASS_PROTO_CBI_I && !sc->intrin) ) {
+ DPRINTF(UDMASS_USB, ("%s: endpoint not found %d/%d/%d\n",
+ device_get_nameunit(sc->sc_dev),
+ sc->bulkin, sc->bulkout, sc->intrin));
+ umass_detach(self);
+ return ENXIO;
+ }
+
+ /* Open the bulk-in and -out pipe */
+ err = usbd_open_pipe(sc->iface, sc->bulkout,
+ USBD_EXCLUSIVE_USE, &sc->bulkout_pipe);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: cannot open %d-out pipe (bulk)\n",
+ device_get_nameunit(sc->sc_dev), sc->bulkout));
+ umass_detach(self);
+ return ENXIO;
+ }
+ err = usbd_open_pipe(sc->iface, sc->bulkin,
+ USBD_EXCLUSIVE_USE, &sc->bulkin_pipe);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: could not open %d-in pipe (bulk)\n",
+ device_get_nameunit(sc->sc_dev), sc->bulkin));
+ umass_detach(self);
+ return ENXIO;
+ }
+ /* Open the intr-in pipe if the protocol is CBI with CCI.
+ * Note: early versions of the Zip drive do have an interrupt pipe, but
+ * this pipe is unused.
+ *
+ * We do not open the interrupt pipe as an interrupt pipe, but as a
+ * normal bulk endpoint. We send an IN transfer down the wire at the
+ * appropriate time, because we know exactly when to expect data on
+ * that endpoint. This saves bandwidth, but more important, makes the
+ * code for handling the data on that endpoint simpler. No data
+ * arriving concurrently.
+ */
+ if (sc->proto & UMASS_PROTO_CBI_I) {
+ err = usbd_open_pipe(sc->iface, sc->intrin,
+ USBD_EXCLUSIVE_USE, &sc->intrin_pipe);
+ if (err) {
+ DPRINTF(UDMASS_USB, ("%s: couldn't open %d-in (intr)\n",
+ device_get_nameunit(sc->sc_dev), sc->intrin));
+ umass_detach(self);
+ return ENXIO;
+ }
+ }
+
+ /* initialisation of generic part */
+ sc->transfer_state = TSTATE_ATTACH;
+
+ /* request a sufficient number of xfer handles */
+ for (i = 0; i < XFER_NR; i++) {
+ sc->transfer_xfer[i] = usbd_alloc_xfer(uaa->device);
+ if (!sc->transfer_xfer[i]) {
+ DPRINTF(UDMASS_USB, ("%s: Out of memory\n",
+ device_get_nameunit(sc->sc_dev)));
+ umass_detach(self);
+ return ENXIO;
+ }
+ }
+
+ /* Initialise the wire protocol specific methods */
+ if (sc->proto & UMASS_PROTO_BBB) {
+ sc->reset = umass_bbb_reset;
+ sc->transfer = umass_bbb_transfer;
+ sc->state = umass_bbb_state;
+ } else if (sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I)) {
+ sc->reset = umass_cbi_reset;
+ sc->transfer = umass_cbi_transfer;
+ sc->state = umass_cbi_state;
+#ifdef USB_DEBUG
+ } else {
+ panic("%s:%d: Unknown proto 0x%02x",
+ __FILE__, __LINE__, sc->proto);
+#endif
+ }
+
+ if (sc->proto & UMASS_PROTO_SCSI)
+ sc->transform = umass_scsi_transform;
+ else if (sc->proto & UMASS_PROTO_UFI)
+ sc->transform = umass_ufi_transform;
+ else if (sc->proto & UMASS_PROTO_ATAPI)
+ sc->transform = umass_atapi_transform;
+ else if (sc->proto & UMASS_PROTO_RBC)
+ sc->transform = umass_rbc_transform;
+#ifdef USB_DEBUG
+ else
+ panic("No transformation defined for command proto 0x%02x",
+ sc->proto & UMASS_PROTO_COMMAND);
+#endif
+
+ /* From here onwards the device can be used. */
+
+ if (sc->quirks & SHUTTLE_INIT)
+ umass_init_shuttle(sc);
+
+ /* Get the maximum LUN supported by the device.
+ */
+ if (((sc->proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB) &&
+ !(sc->quirks & NO_GETMAXLUN))
+ sc->maxlun = umass_bbb_get_max_lun(sc);
+ else
+ sc->maxlun = 0;
+
+ if ((sc->proto & UMASS_PROTO_SCSI) ||
+ (sc->proto & UMASS_PROTO_ATAPI) ||
+ (sc->proto & UMASS_PROTO_UFI) ||
+ (sc->proto & UMASS_PROTO_RBC)) {
+ /* Prepare the SCSI command block */
+ sc->cam_scsi_sense.opcode = REQUEST_SENSE;
+ sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY;
+
+ /* register the SIM */
+ err = umass_cam_attach_sim(sc);
+ if (err) {
+ umass_detach(self);
+ return ENXIO;
+ }
+ /* scan the new sim */
+ err = umass_cam_attach(sc);
+ if (err) {
+ umass_cam_detach_sim(sc);
+ umass_detach(self);
+ return ENXIO;
+ }
+ } else {
+ panic("%s:%d: Unknown proto 0x%02x",
+ __FILE__, __LINE__, sc->proto);
+ }
+
+ sc->transfer_state = TSTATE_IDLE;
+ DPRINTF(UDMASS_GEN, ("%s: Attach finished\n", device_get_nameunit(sc->sc_dev)));
+
+ return 0;
+}
+
+static int
+umass_detach(device_t self)
+{
+ struct umass_softc *sc = device_get_softc(self);
+ int err = 0;
+ int i;
+
+ DPRINTF(UDMASS_USB, ("%s: detached\n", device_get_nameunit(sc->sc_dev)));
+
+ sc->flags |= UMASS_FLAGS_GONE;
+
+ /* abort all the pipes in case there are transfers active. */
+ usbd_abort_default_pipe(sc->sc_udev);
+ if (sc->bulkout_pipe)
+ usbd_abort_pipe(sc->bulkout_pipe);
+ if (sc->bulkin_pipe)
+ usbd_abort_pipe(sc->bulkin_pipe);
+ if (sc->intrin_pipe)
+ usbd_abort_pipe(sc->intrin_pipe);
+
+ callout_drain(&sc->cam_scsi_rescan_ch);
+ if ((sc->proto & UMASS_PROTO_SCSI) ||
+ (sc->proto & UMASS_PROTO_ATAPI) ||
+ (sc->proto & UMASS_PROTO_UFI) ||
+ (sc->proto & UMASS_PROTO_RBC))
+ /* detach the SCSI host controller (SIM) */
+ err = umass_cam_detach_sim(sc);
+
+ for (i = 0; i < XFER_NR; i++)
+ if (sc->transfer_xfer[i])
+ usbd_free_xfer(sc->transfer_xfer[i]);
+
+ /* remove all the pipes */
+ if (sc->bulkout_pipe)
+ usbd_close_pipe(sc->bulkout_pipe);
+ if (sc->bulkin_pipe)
+ usbd_close_pipe(sc->bulkin_pipe);
+ if (sc->intrin_pipe)
+ usbd_close_pipe(sc->intrin_pipe);
+
+ return(err);
+}
+
+static void
+umass_init_shuttle(struct umass_softc *sc)
+{
+ usb_device_request_t req;
+ u_char status[2];
+
+ /* The Linux driver does this, but no one can tell us what the
+ * command does.
+ */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = 1; /* XXX unknown command */
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->ifaceno);
+ USETW(req.wLength, sizeof status);
+ (void) usbd_do_request(sc->sc_udev, &req, &status);
+
+ DPRINTF(UDMASS_GEN, ("%s: Shuttle init returned 0x%02x%02x\n",
+ device_get_nameunit(sc->sc_dev), status[0], status[1]));
+}
+
+ /*
+ * Generic functions to handle transfers
+ */
+
+static usbd_status
+umass_setup_transfer(struct umass_softc *sc, usbd_pipe_handle pipe,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Initialise a USB transfer and then schedule it */
+
+ (void) usbd_setup_xfer(xfer, pipe, (void *) sc, buffer, buflen, flags,
+ sc->timeout, sc->state);
+
+ err = usbd_transfer(xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(UDMASS_BBB, ("%s: failed to setup transfer, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err)));
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+
+static usbd_status
+umass_setup_ctrl_transfer(struct umass_softc *sc, usbd_device_handle udev,
+ usb_device_request_t *req,
+ void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ /* Initialise a USB control transfer and then schedule it */
+
+ (void) usbd_setup_default_xfer(xfer, udev, (void *) sc,
+ sc->timeout, req, buffer, buflen, flags, sc->state);
+
+ err = usbd_transfer(xfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(UDMASS_BBB, ("%s: failed to setup ctrl transfer, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err)));
+
+ /* do not reset, as this would make us loop */
+ return(err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static void
+umass_clear_endpoint_stall(struct umass_softc *sc,
+ u_int8_t endpt, usbd_pipe_handle pipe,
+ int state, usbd_xfer_handle xfer)
+{
+ usbd_device_handle udev;
+
+ DPRINTF(UDMASS_BBB, ("%s: Clear endpoint 0x%02x stall\n",
+ device_get_nameunit(sc->sc_dev), endpt));
+
+ usbd_interface2device_handle(sc->iface, &udev);
+
+ sc->transfer_state = state;
+
+ usbd_clear_endpoint_toggle(pipe);
+
+ sc->request.bmRequestType = UT_WRITE_ENDPOINT;
+ sc->request.bRequest = UR_CLEAR_FEATURE;
+ USETW(sc->request.wValue, UF_ENDPOINT_HALT);
+ USETW(sc->request.wIndex, endpt);
+ USETW(sc->request.wLength, 0);
+ umass_setup_ctrl_transfer(sc, udev, &sc->request, NULL, 0, 0, xfer);
+}
+
+static void
+umass_reset(struct umass_softc *sc, transfer_cb_f cb, void *priv)
+{
+ sc->transfer_cb = cb;
+ sc->transfer_priv = priv;
+
+ /* The reset is a forced reset, so no error (yet) */
+ sc->reset(sc, STATUS_CMD_OK);
+}
+
+/*
+ * Bulk protocol specific functions
+ */
+
+static void
+umass_bbb_reset(struct umass_softc *sc, int status)
+{
+ usbd_device_handle udev;
+
+ KASSERT(sc->proto & UMASS_PROTO_BBB,
+ ("%s: umass_bbb_reset: wrong sc->proto 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), sc->proto));
+
+ /*
+ * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class)
+ *
+ * For Reset Recovery the host shall issue in the following order:
+ * a) a Bulk-Only Mass Storage Reset
+ * b) a Clear Feature HALT to the Bulk-In endpoint
+ * c) a Clear Feature HALT to the Bulk-Out endpoint
+ *
+ * This is done in 3 steps, states:
+ * TSTATE_BBB_RESET1
+ * TSTATE_BBB_RESET2
+ * TSTATE_BBB_RESET3
+ *
+ * If the reset doesn't succeed, the device should be port reset.
+ */
+
+ DPRINTF(UDMASS_BBB, ("%s: Bulk Reset\n",
+ device_get_nameunit(sc->sc_dev)));
+
+ sc->transfer_state = TSTATE_BBB_RESET1;
+ sc->transfer_status = status;
+
+ usbd_interface2device_handle(sc->iface, &udev);
+
+ /* reset is a class specific interface write */
+ sc->request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ sc->request.bRequest = UR_BBB_RESET;
+ USETW(sc->request.wValue, 0);
+ USETW(sc->request.wIndex, sc->ifaceno);
+ USETW(sc->request.wLength, 0);
+ umass_setup_ctrl_transfer(sc, udev, &sc->request, NULL, 0, 0,
+ sc->transfer_xfer[XFER_BBB_RESET1]);
+}
+
+static void
+umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen,
+ void *data, int datalen, int dir, u_int timeout,
+ transfer_cb_f cb, void *priv)
+{
+ KASSERT(sc->proto & UMASS_PROTO_BBB,
+ ("%s: umass_bbb_transfer: wrong sc->proto 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), sc->proto));
+
+ /* Be a little generous. */
+ sc->timeout = timeout + UMASS_TIMEOUT;
+
+ /*
+ * Do a Bulk-Only transfer with cmdlen bytes from cmd, possibly
+ * a data phase of datalen bytes from/to the device and finally a
+ * csw read phase.
+ * If the data direction was inbound a maximum of datalen bytes
+ * is stored in the buffer pointed to by data.
+ *
+ * umass_bbb_transfer initialises the transfer and lets the state
+ * machine in umass_bbb_state handle the completion. It uses the
+ * following states:
+ * TSTATE_BBB_COMMAND
+ * -> TSTATE_BBB_DATA
+ * -> TSTATE_BBB_STATUS
+ * -> TSTATE_BBB_STATUS2
+ * -> TSTATE_BBB_IDLE
+ *
+ * An error in any of those states will invoke
+ * umass_bbb_reset.
+ */
+
+ /* check the given arguments */
+ KASSERT(datalen == 0 || data != NULL,
+ ("%s: datalen > 0, but no buffer",device_get_nameunit(sc->sc_dev)));
+ KASSERT(cmdlen <= CBWCDBLENGTH,
+ ("%s: cmdlen exceeds CDB length in CBW (%d > %d)",
+ device_get_nameunit(sc->sc_dev), cmdlen, CBWCDBLENGTH));
+ KASSERT(dir == DIR_NONE || datalen > 0,
+ ("%s: datalen == 0 while direction is not NONE\n",
+ device_get_nameunit(sc->sc_dev)));
+ KASSERT(datalen == 0 || dir != DIR_NONE,
+ ("%s: direction is NONE while datalen is not zero\n",
+ device_get_nameunit(sc->sc_dev)));
+ KASSERT(sizeof(umass_bbb_cbw_t) == UMASS_BBB_CBW_SIZE,
+ ("%s: CBW struct does not have the right size (%ld vs. %d)\n",
+ device_get_nameunit(sc->sc_dev),
+ (long)sizeof(umass_bbb_cbw_t), UMASS_BBB_CBW_SIZE));
+ KASSERT(sizeof(umass_bbb_csw_t) == UMASS_BBB_CSW_SIZE,
+ ("%s: CSW struct does not have the right size (%ld vs. %d)\n",
+ device_get_nameunit(sc->sc_dev),
+ (long)sizeof(umass_bbb_csw_t), UMASS_BBB_CSW_SIZE));
+
+ /*
+ * Determine the direction of the data transfer and the length.
+ *
+ * dCBWDataTransferLength (datalen) :
+ * This field indicates the number of bytes of data that the host
+ * intends to transfer on the IN or OUT Bulk endpoint(as indicated by
+ * the Direction bit) during the execution of this command. If this
+ * field is set to 0, the device will expect that no data will be
+ * transferred IN or OUT during this command, regardless of the value
+ * of the Direction bit defined in dCBWFlags.
+ *
+ * dCBWFlags (dir) :
+ * The bits of the Flags field are defined as follows:
+ * Bits 0-6 reserved
+ * Bit 7 Direction - this bit shall be ignored if the
+ * dCBWDataTransferLength field is zero.
+ * 0 = data Out from host to device
+ * 1 = data In from device to host
+ */
+
+ /* Fill in the Command Block Wrapper
+ * We fill in all the fields, so there is no need to bzero it first.
+ */
+ USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
+ /* We don't care about the initial value, as long as the values are unique */
+ USETDW(sc->cbw.dCBWTag, UGETDW(sc->cbw.dCBWTag) + 1);
+ USETDW(sc->cbw.dCBWDataTransferLength, datalen);
+ /* DIR_NONE is treated as DIR_OUT (0x00) */
+ sc->cbw.bCBWFlags = (dir == DIR_IN? CBWFLAGS_IN:CBWFLAGS_OUT);
+ sc->cbw.bCBWLUN = lun;
+ sc->cbw.bCDBLength = cmdlen;
+ bcopy(cmd, sc->cbw.CBWCDB, cmdlen);
+
+ DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw));
+
+ /* store the details for the data transfer phase */
+ sc->transfer_dir = dir;
+ sc->transfer_data = data;
+ sc->transfer_datalen = datalen;
+ sc->transfer_actlen = 0;
+ sc->transfer_cb = cb;
+ sc->transfer_priv = priv;
+ sc->transfer_status = STATUS_CMD_OK;
+
+ /* move from idle to the command state */
+ sc->transfer_state = TSTATE_BBB_COMMAND;
+
+ /* Send the CBW from host to device via bulk-out endpoint. */
+ if (umass_setup_transfer(sc, sc->bulkout_pipe,
+ &sc->cbw, UMASS_BBB_CBW_SIZE, 0,
+ sc->transfer_xfer[XFER_BBB_CBW])) {
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ }
+}
+
+
+static void
+umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ struct umass_softc *sc = (struct umass_softc *) priv;
+ usbd_xfer_handle next_xfer;
+
+ KASSERT(sc->proto & UMASS_PROTO_BBB,
+ ("%s: umass_bbb_state: wrong sc->proto 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), sc->proto));
+
+ /*
+ * State handling for BBB transfers.
+ *
+ * The subroutine is rather long. It steps through the states given in
+ * Annex A of the Bulk-Only specification.
+ * Each state first does the error handling of the previous transfer
+ * and then prepares the next transfer.
+ * Each transfer is done asynchronously so after the request/transfer
+ * has been submitted you will find a 'return;'.
+ */
+
+ DPRINTF(UDMASS_BBB, ("%s: Handling BBB state %d (%s), xfer=%p, %s\n",
+ device_get_nameunit(sc->sc_dev), sc->transfer_state,
+ states[sc->transfer_state], xfer, usbd_errstr(err)));
+
+ /* Give up if the device has detached. */
+ if (sc->flags & UMASS_FLAGS_GONE) {
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv, sc->transfer_datalen,
+ STATUS_CMD_FAILED);
+ return;
+ }
+
+ switch (sc->transfer_state) {
+
+ /***** Bulk Transfer *****/
+ case TSTATE_BBB_COMMAND:
+ /* Command transport phase, error handling */
+ if (err) {
+ DPRINTF(UDMASS_BBB, ("%s: failed to send CBW\n",
+ device_get_nameunit(sc->sc_dev)));
+ /* If the device detects that the CBW is invalid, then
+ * the device may STALL both bulk endpoints and require
+ * a Bulk-Reset
+ */
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+
+ /* Data transport phase, setup transfer */
+ sc->transfer_state = TSTATE_BBB_DATA;
+ if (sc->transfer_dir == DIR_IN) {
+ if (umass_setup_transfer(sc, sc->bulkin_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ USBD_SHORT_XFER_OK,
+ sc->transfer_xfer[XFER_BBB_DATA]))
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+
+ return;
+ } else if (sc->transfer_dir == DIR_OUT) {
+ if (umass_setup_transfer(sc, sc->bulkout_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_BBB_DATA]))
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+
+ return;
+ } else {
+ DPRINTF(UDMASS_BBB, ("%s: no data phase\n",
+ device_get_nameunit(sc->sc_dev)));
+ }
+
+ /* FALLTHROUGH if no data phase, err == 0 */
+ case TSTATE_BBB_DATA:
+ /* Command transport phase, error handling (ignored if no data
+ * phase (fallthrough from previous state)) */
+ if (sc->transfer_dir != DIR_NONE) {
+ /* retrieve the length of the transfer that was done */
+ usbd_get_xfer_status(xfer, NULL, NULL,
+ &sc->transfer_actlen, NULL);
+
+ if (err) {
+ DPRINTF(UDMASS_BBB, ("%s: Data-%s %db failed, "
+ "%s\n", device_get_nameunit(sc->sc_dev),
+ (sc->transfer_dir == DIR_IN?"in":"out"),
+ sc->transfer_datalen,usbd_errstr(err)));
+
+ if (err == USBD_STALLED) {
+ umass_clear_endpoint_stall(sc,
+ (sc->transfer_dir == DIR_IN?
+ sc->bulkin:sc->bulkout),
+ (sc->transfer_dir == DIR_IN?
+ sc->bulkin_pipe:sc->bulkout_pipe),
+ TSTATE_BBB_DCLEAR,
+ sc->transfer_xfer[XFER_BBB_DCLEAR]);
+ return;
+ } else {
+ /* Unless the error is a pipe stall the
+ * error is fatal.
+ */
+ umass_bbb_reset(sc,STATUS_WIRE_FAILED);
+ return;
+ }
+ }
+ }
+
+ DIF(UDMASS_BBB, if (sc->transfer_dir == DIR_IN)
+ umass_dump_buffer(sc, sc->transfer_data,
+ sc->transfer_datalen, 48));
+
+
+
+ /* FALLTHROUGH, err == 0 (no data phase or successfull) */
+ case TSTATE_BBB_DCLEAR: /* stall clear after data phase */
+ case TSTATE_BBB_SCLEAR: /* stall clear after status phase */
+ /* Reading of CSW after bulk stall condition in data phase
+ * (TSTATE_BBB_DATA2) or bulk-in stall condition after
+ * reading CSW (TSTATE_BBB_SCLEAR).
+ * In the case of no data phase or successfull data phase,
+ * err == 0 and the following if block is passed.
+ */
+ if (err) { /* should not occur */
+ /* try the transfer below, even if clear stall failed */
+ DPRINTF(UDMASS_BBB, ("%s: bulk-%s stall clear failed"
+ ", %s\n", device_get_nameunit(sc->sc_dev),
+ (sc->transfer_dir == DIR_IN? "in":"out"),
+ usbd_errstr(err)));
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+
+ /* Status transport phase, setup transfer */
+ if (sc->transfer_state == TSTATE_BBB_COMMAND ||
+ sc->transfer_state == TSTATE_BBB_DATA ||
+ sc->transfer_state == TSTATE_BBB_DCLEAR) {
+ /* After no data phase, successfull data phase and
+ * after clearing bulk-in/-out stall condition
+ */
+ sc->transfer_state = TSTATE_BBB_STATUS1;
+ next_xfer = sc->transfer_xfer[XFER_BBB_CSW1];
+ } else {
+ /* After first attempt of fetching CSW */
+ sc->transfer_state = TSTATE_BBB_STATUS2;
+ next_xfer = sc->transfer_xfer[XFER_BBB_CSW2];
+ }
+
+ /* Read the Command Status Wrapper via bulk-in endpoint. */
+ if (umass_setup_transfer(sc, sc->bulkin_pipe,
+ &sc->csw, UMASS_BBB_CSW_SIZE, 0,
+ next_xfer)) {
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+
+ return;
+ case TSTATE_BBB_STATUS1: /* first attempt */
+ case TSTATE_BBB_STATUS2: /* second attempt */
+ /* Status transfer, error handling */
+ if (err) {
+ DPRINTF(UDMASS_BBB, ("%s: Failed to read CSW, %s%s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err),
+ (sc->transfer_state == TSTATE_BBB_STATUS1?
+ ", retrying":"")));
+
+ /* If this was the first attempt at fetching the CSW
+ * retry it, otherwise fail.
+ */
+ if (sc->transfer_state == TSTATE_BBB_STATUS1) {
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe,
+ TSTATE_BBB_SCLEAR,
+ sc->transfer_xfer[XFER_BBB_SCLEAR]);
+ return;
+ } else {
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ }
+ }
+
+ DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw));
+
+ /* Translate weird command-status signatures. */
+ if (sc->quirks & WRONG_CSWSIG) {
+ u_int32_t dCSWSignature = UGETDW(sc->csw.dCSWSignature);
+ if (dCSWSignature == CSWSIGNATURE_OLYMPUS_C1 ||
+ dCSWSignature == CSWSIGNATURE_IMAGINATION_DBX1)
+ USETDW(sc->csw.dCSWSignature, CSWSIGNATURE);
+ }
+
+ int Residue;
+ Residue = UGETDW(sc->csw.dCSWDataResidue);
+ if (Residue == 0 &&
+ sc->transfer_datalen - sc->transfer_actlen != 0)
+ Residue = sc->transfer_datalen - sc->transfer_actlen;
+
+ /* Check CSW and handle any error */
+ if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) {
+ /* Invalid CSW: Wrong signature or wrong tag might
+ * indicate that the device is confused -> reset it.
+ */
+ printf("%s: Invalid CSW: sig 0x%08x should be 0x%08x\n",
+ device_get_nameunit(sc->sc_dev),
+ UGETDW(sc->csw.dCSWSignature),
+ CSWSIGNATURE);
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ } else if (UGETDW(sc->csw.dCSWTag)
+ != UGETDW(sc->cbw.dCBWTag)) {
+ printf("%s: Invalid CSW: tag %d should be %d\n",
+ device_get_nameunit(sc->sc_dev),
+ UGETDW(sc->csw.dCSWTag),
+ UGETDW(sc->cbw.dCBWTag));
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+
+ /* CSW is valid here */
+ } else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) {
+ printf("%s: Invalid CSW: status %d > %d\n",
+ device_get_nameunit(sc->sc_dev),
+ sc->csw.bCSWStatus,
+ CSWSTATUS_PHASE);
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+ } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) {
+ printf("%s: Phase Error, residue = %d\n",
+ device_get_nameunit(sc->sc_dev), Residue);
+
+ umass_bbb_reset(sc, STATUS_WIRE_FAILED);
+ return;
+
+ } else if (sc->transfer_actlen > sc->transfer_datalen) {
+ /* Buffer overrun! Don't let this go by unnoticed */
+ panic("%s: transferred %db instead of %db",
+ device_get_nameunit(sc->sc_dev),
+ sc->transfer_actlen, sc->transfer_datalen);
+
+ } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) {
+ DPRINTF(UDMASS_BBB, ("%s: Command Failed, res = %d\n",
+ device_get_nameunit(sc->sc_dev), Residue));
+
+ /* SCSI command failed but transfer was succesful */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv, Residue,
+ STATUS_CMD_FAILED);
+ return;
+
+ } else { /* success */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv, Residue,
+ STATUS_CMD_OK);
+
+ return;
+ }
+
+ /***** Bulk Reset *****/
+ case TSTATE_BBB_RESET1:
+ if (err)
+ printf("%s: BBB reset failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe, TSTATE_BBB_RESET2,
+ sc->transfer_xfer[XFER_BBB_RESET2]);
+
+ return;
+ case TSTATE_BBB_RESET2:
+ if (err) /* should not occur */
+ printf("%s: BBB bulk-in clear stall failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkout, sc->bulkout_pipe, TSTATE_BBB_RESET3,
+ sc->transfer_xfer[XFER_BBB_RESET3]);
+
+ return;
+ case TSTATE_BBB_RESET3:
+ if (err) /* should not occur */
+ printf("%s: BBB bulk-out clear stall failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ sc->transfer_state = TSTATE_IDLE;
+ if (sc->transfer_priv) {
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ sc->transfer_status);
+ }
+
+ return;
+
+ /***** Default *****/
+ default:
+ panic("%s: Unknown state %d",
+ device_get_nameunit(sc->sc_dev), sc->transfer_state);
+ }
+}
+
+static int
+umass_bbb_get_max_lun(struct umass_softc *sc)
+{
+ usbd_device_handle udev;
+ usb_device_request_t req;
+ usbd_status err;
+ usb_interface_descriptor_t *id;
+ int maxlun = 0;
+ u_int8_t buf = 0;
+
+ usbd_interface2device_handle(sc->iface, &udev);
+ id = usbd_get_interface_descriptor(sc->iface);
+
+ /* The Get Max Lun command is a class-specific request. */
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_BBB_GET_MAX_LUN;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, id->bInterfaceNumber);
+ USETW(req.wLength, 1);
+
+ err = usbd_do_request(udev, &req, &buf);
+ switch (err) {
+ case USBD_NORMAL_COMPLETION:
+ maxlun = buf;
+ DPRINTF(UDMASS_BBB, ("%s: Max Lun is %d\n",
+ device_get_nameunit(sc->sc_dev), maxlun));
+ break;
+ case USBD_STALLED:
+ case USBD_SHORT_XFER:
+ default:
+ /* Device doesn't support Get Max Lun request. */
+ printf("%s: Get Max Lun not supported (%s)\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ /* XXX Should we port_reset the device? */
+ break;
+ }
+
+ return(maxlun);
+}
+
+/*
+ * Command/Bulk/Interrupt (CBI) specific functions
+ */
+
+static int
+umass_cbi_adsc(struct umass_softc *sc, char *buffer, int buflen,
+ usbd_xfer_handle xfer)
+{
+ usbd_device_handle udev;
+
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_adsc: wrong sc->proto 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), sc->proto));
+
+ usbd_interface2device_handle(sc->iface, &udev);
+
+ sc->request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ sc->request.bRequest = UR_CBI_ADSC;
+ USETW(sc->request.wValue, 0);
+ USETW(sc->request.wIndex, sc->ifaceno);
+ USETW(sc->request.wLength, buflen);
+ return umass_setup_ctrl_transfer(sc, udev, &sc->request, buffer,
+ buflen, 0, xfer);
+}
+
+
+static void
+umass_cbi_reset(struct umass_softc *sc, int status)
+{
+ int i;
+# define SEND_DIAGNOSTIC_CMDLEN 12
+
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_reset: wrong sc->proto 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), sc->proto));
+
+ /*
+ * Command Block Reset Protocol
+ *
+ * First send a reset request to the device. Then clear
+ * any possibly stalled bulk endpoints.
+ *
+ * This is done in 3 steps, states:
+ * TSTATE_CBI_RESET1
+ * TSTATE_CBI_RESET2
+ * TSTATE_CBI_RESET3
+ *
+ * If the reset doesn't succeed, the device should be port reset.
+ */
+
+ DPRINTF(UDMASS_CBI, ("%s: CBI Reset\n",
+ device_get_nameunit(sc->sc_dev)));
+
+ KASSERT(sizeof(sc->cbl) >= SEND_DIAGNOSTIC_CMDLEN,
+ ("%s: CBL struct is too small (%ld < %d)\n",
+ device_get_nameunit(sc->sc_dev),
+ (long)sizeof(sc->cbl), SEND_DIAGNOSTIC_CMDLEN));
+
+ sc->transfer_state = TSTATE_CBI_RESET1;
+ sc->transfer_status = status;
+
+ /* The 0x1d code is the SEND DIAGNOSTIC command. To distinguish between
+ * the two the last 10 bytes of the cbl is filled with 0xff (section
+ * 2.2 of the CBI spec).
+ */
+ sc->cbl[0] = 0x1d; /* Command Block Reset */
+ sc->cbl[1] = 0x04;
+ for (i = 2; i < SEND_DIAGNOSTIC_CMDLEN; i++)
+ sc->cbl[i] = 0xff;
+
+ umass_cbi_adsc(sc, sc->cbl, SEND_DIAGNOSTIC_CMDLEN,
+ sc->transfer_xfer[XFER_CBI_RESET1]);
+ /* XXX if the command fails we should reset the port on the hub */
+}
+
+static void
+umass_cbi_transfer(struct umass_softc *sc, int lun,
+ void *cmd, int cmdlen, void *data, int datalen, int dir,
+ u_int timeout, transfer_cb_f cb, void *priv)
+{
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_transfer: wrong sc->proto 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), sc->proto));
+
+ /* Be a little generous. */
+ sc->timeout = timeout + UMASS_TIMEOUT;
+
+ /*
+ * Do a CBI transfer with cmdlen bytes from cmd, possibly
+ * a data phase of datalen bytes from/to the device and finally a
+ * csw read phase.
+ * If the data direction was inbound a maximum of datalen bytes
+ * is stored in the buffer pointed to by data.
+ *
+ * umass_cbi_transfer initialises the transfer and lets the state
+ * machine in umass_cbi_state handle the completion. It uses the
+ * following states:
+ * TSTATE_CBI_COMMAND
+ * -> XXX fill in
+ *
+ * An error in any of those states will invoke
+ * umass_cbi_reset.
+ */
+
+ /* check the given arguments */
+ KASSERT(datalen == 0 || data != NULL,
+ ("%s: datalen > 0, but no buffer",device_get_nameunit(sc->sc_dev)));
+ KASSERT(datalen == 0 || dir != DIR_NONE,
+ ("%s: direction is NONE while datalen is not zero\n",
+ device_get_nameunit(sc->sc_dev)));
+
+ /* store the details for the data transfer phase */
+ sc->transfer_dir = dir;
+ sc->transfer_data = data;
+ sc->transfer_datalen = datalen;
+ sc->transfer_actlen = 0;
+ sc->transfer_cb = cb;
+ sc->transfer_priv = priv;
+ sc->transfer_status = STATUS_CMD_OK;
+
+ /* move from idle to the command state */
+ sc->transfer_state = TSTATE_CBI_COMMAND;
+
+ DIF(UDMASS_CBI, umass_cbi_dump_cmd(sc, cmd, cmdlen));
+
+ /* Send the Command Block from host to device via control endpoint. */
+ if (umass_cbi_adsc(sc, cmd, cmdlen, sc->transfer_xfer[XFER_CBI_CB]))
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+}
+
+static void
+umass_cbi_state(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ struct umass_softc *sc = (struct umass_softc *) priv;
+
+ KASSERT(sc->proto & (UMASS_PROTO_CBI|UMASS_PROTO_CBI_I),
+ ("%s: umass_cbi_state: wrong sc->proto 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), sc->proto));
+
+ /*
+ * State handling for CBI transfers.
+ */
+
+ DPRINTF(UDMASS_CBI, ("%s: Handling CBI state %d (%s), xfer=%p, %s\n",
+ device_get_nameunit(sc->sc_dev), sc->transfer_state,
+ states[sc->transfer_state], xfer, usbd_errstr(err)));
+
+ /* Give up if the device has detached. */
+ if (sc->flags & UMASS_FLAGS_GONE) {
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv, sc->transfer_datalen,
+ STATUS_CMD_FAILED);
+ return;
+ }
+
+ switch (sc->transfer_state) {
+
+ /***** CBI Transfer *****/
+ case TSTATE_CBI_COMMAND:
+ if (err == USBD_STALLED) {
+ DPRINTF(UDMASS_CBI, ("%s: Command Transport failed\n",
+ device_get_nameunit(sc->sc_dev)));
+ /* Status transport by control pipe (section 2.3.2.1).
+ * The command contained in the command block failed.
+ *
+ * The control pipe has already been unstalled by the
+ * USB stack.
+ * Section 2.4.3.1.1 states that the bulk in endpoints
+ * should not be stalled at this point.
+ */
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ STATUS_CMD_FAILED);
+
+ return;
+ } else if (err) {
+ DPRINTF(UDMASS_CBI, ("%s: failed to send ADSC\n",
+ device_get_nameunit(sc->sc_dev)));
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+
+ return;
+ }
+
+ sc->transfer_state = TSTATE_CBI_DATA;
+ if (sc->transfer_dir == DIR_IN) {
+ if (umass_setup_transfer(sc, sc->bulkin_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ USBD_SHORT_XFER_OK,
+ sc->transfer_xfer[XFER_CBI_DATA]))
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+
+ } else if (sc->transfer_dir == DIR_OUT) {
+ if (umass_setup_transfer(sc, sc->bulkout_pipe,
+ sc->transfer_data, sc->transfer_datalen,
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_CBI_DATA]))
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+
+ } else if (sc->proto & UMASS_PROTO_CBI_I) {
+ DPRINTF(UDMASS_CBI, ("%s: no data phase\n",
+ device_get_nameunit(sc->sc_dev)));
+ sc->transfer_state = TSTATE_CBI_STATUS;
+ if (umass_setup_transfer(sc, sc->intrin_pipe,
+ &sc->sbl, sizeof(sc->sbl),
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_CBI_STATUS])){
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ } else {
+ DPRINTF(UDMASS_CBI, ("%s: no data phase\n",
+ device_get_nameunit(sc->sc_dev)));
+ /* No command completion interrupt. Request
+ * sense data.
+ */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ 0, STATUS_CMD_UNKNOWN);
+ }
+
+ return;
+
+ case TSTATE_CBI_DATA:
+ /* retrieve the length of the transfer that was done */
+ usbd_get_xfer_status(xfer,NULL,NULL,&sc->transfer_actlen,NULL);
+
+ if (err) {
+ DPRINTF(UDMASS_CBI, ("%s: Data-%s %db failed, "
+ "%s\n", device_get_nameunit(sc->sc_dev),
+ (sc->transfer_dir == DIR_IN?"in":"out"),
+ sc->transfer_datalen,usbd_errstr(err)));
+
+ if (err == USBD_STALLED) {
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe,
+ TSTATE_CBI_DCLEAR,
+ sc->transfer_xfer[XFER_CBI_DCLEAR]);
+ } else {
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ return;
+ }
+
+ DIF(UDMASS_CBI, if (sc->transfer_dir == DIR_IN)
+ umass_dump_buffer(sc, sc->transfer_data,
+ sc->transfer_actlen, 48));
+
+ if (sc->proto & UMASS_PROTO_CBI_I) {
+ sc->transfer_state = TSTATE_CBI_STATUS;
+ if (umass_setup_transfer(sc, sc->intrin_pipe,
+ &sc->sbl, sizeof(sc->sbl),
+ 0, /* fixed length transfer */
+ sc->transfer_xfer[XFER_CBI_STATUS])){
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ } else {
+ /* No command completion interrupt. Request
+ * sense to get status of command.
+ */
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen - sc->transfer_actlen,
+ STATUS_CMD_UNKNOWN);
+ }
+ return;
+
+ case TSTATE_CBI_STATUS:
+ if (err) {
+ DPRINTF(UDMASS_CBI, ("%s: Status Transport failed\n",
+ device_get_nameunit(sc->sc_dev)));
+ /* Status transport by interrupt pipe (section 2.3.2.2).
+ */
+
+ if (err == USBD_STALLED) {
+ umass_clear_endpoint_stall(sc,
+ sc->intrin, sc->intrin_pipe,
+ TSTATE_CBI_SCLEAR,
+ sc->transfer_xfer[XFER_CBI_SCLEAR]);
+ } else {
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+ return;
+ }
+
+ /* Dissect the information in the buffer */
+
+ if (sc->proto & UMASS_PROTO_UFI) {
+ int status;
+
+ /* Section 3.4.3.1.3 specifies that the UFI command
+ * protocol returns an ASC and ASCQ in the interrupt
+ * data block.
+ */
+
+ DPRINTF(UDMASS_CBI, ("%s: UFI CCI, ASC = 0x%02x, "
+ "ASCQ = 0x%02x\n",
+ device_get_nameunit(sc->sc_dev),
+ sc->sbl.ufi.asc, sc->sbl.ufi.ascq));
+
+ if (sc->sbl.ufi.asc == 0 && sc->sbl.ufi.ascq == 0)
+ status = STATUS_CMD_OK;
+ else
+ status = STATUS_CMD_FAILED;
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen - sc->transfer_actlen,
+ status);
+ } else {
+ /* Command Interrupt Data Block */
+ DPRINTF(UDMASS_CBI, ("%s: type=0x%02x, value=0x%02x\n",
+ device_get_nameunit(sc->sc_dev),
+ sc->sbl.common.type, sc->sbl.common.value));
+
+ if (sc->sbl.common.type == IDB_TYPE_CCI) {
+ int err;
+
+ if ((sc->sbl.common.value&IDB_VALUE_STATUS_MASK)
+ == IDB_VALUE_PASS) {
+ err = STATUS_CMD_OK;
+ } else if ((sc->sbl.common.value & IDB_VALUE_STATUS_MASK)
+ == IDB_VALUE_FAIL ||
+ (sc->sbl.common.value & IDB_VALUE_STATUS_MASK)
+ == IDB_VALUE_PERSISTENT) {
+ err = STATUS_CMD_FAILED;
+ } else {
+ err = STATUS_WIRE_FAILED;
+ }
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen-sc->transfer_actlen,
+ err);
+ }
+ }
+ return;
+
+ case TSTATE_CBI_DCLEAR:
+ if (err) { /* should not occur */
+ printf("%s: CBI bulk-in/out stall clear failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+ }
+
+ sc->transfer_state = TSTATE_IDLE;
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ STATUS_CMD_FAILED);
+ return;
+
+ case TSTATE_CBI_SCLEAR:
+ if (err) /* should not occur */
+ printf("%s: CBI intr-in stall clear failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+
+ /* Something really bad is going on. Reset the device */
+ umass_cbi_reset(sc, STATUS_CMD_FAILED);
+ return;
+
+ /***** CBI Reset *****/
+ case TSTATE_CBI_RESET1:
+ if (err)
+ printf("%s: CBI reset failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkin, sc->bulkin_pipe, TSTATE_CBI_RESET2,
+ sc->transfer_xfer[XFER_CBI_RESET2]);
+
+ return;
+ case TSTATE_CBI_RESET2:
+ if (err) /* should not occur */
+ printf("%s: CBI bulk-in stall clear failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ umass_clear_endpoint_stall(sc,
+ sc->bulkout, sc->bulkout_pipe, TSTATE_CBI_RESET3,
+ sc->transfer_xfer[XFER_CBI_RESET3]);
+
+ return;
+ case TSTATE_CBI_RESET3:
+ if (err) /* should not occur */
+ printf("%s: CBI bulk-out stall clear failed, %s\n",
+ device_get_nameunit(sc->sc_dev), usbd_errstr(err));
+ /* no error recovery, otherwise we end up in a loop */
+
+ sc->transfer_state = TSTATE_IDLE;
+ if (sc->transfer_priv) {
+ sc->transfer_cb(sc, sc->transfer_priv,
+ sc->transfer_datalen,
+ sc->transfer_status);
+ }
+
+ return;
+
+
+ /***** Default *****/
+ default:
+ panic("%s: Unknown state %d",
+ device_get_nameunit(sc->sc_dev), sc->transfer_state);
+ }
+}
+
+
+
+
+/*
+ * CAM specific functions (used by SCSI, UFI, 8070i (ATAPI))
+ */
+
+static int
+umass_cam_attach_sim(struct umass_softc *sc)
+{
+ struct cam_devq *devq; /* Per device Queue */
+
+ /* A HBA is attached to the CAM layer.
+ *
+ * The CAM layer will then after a while start probing for
+ * devices on the bus. The number of SIMs is limited to one.
+ */
+
+ devq = cam_simq_alloc(1 /*maximum openings*/);
+ if (devq == NULL)
+ return(ENOMEM);
+
+ sc->umass_sim = cam_sim_alloc(umass_cam_action, umass_cam_poll,
+ DEVNAME_SIM,
+ sc /*priv*/,
+ device_get_unit(sc->sc_dev) /*unit number*/,
+ &Giant,
+ 1 /*maximum device openings*/,
+ 0 /*maximum tagged device openings*/,
+ devq);
+ if (sc->umass_sim == NULL) {
+ cam_simq_free(devq);
+ return(ENOMEM);
+ }
+
+ if(xpt_bus_register(sc->umass_sim, NULL, device_get_unit(sc->sc_dev)) !=
+ CAM_SUCCESS)
+ return(ENOMEM);
+
+ return(0);
+}
+
+static void
+umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+#ifdef USB_DEBUG
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d Rescan failed, 0x%04x\n",
+ periph->periph_name, periph->unit_number,
+ ccb->ccb_h.status));
+ } else {
+ DPRINTF(UDMASS_SCSI, ("%s%d: Rescan succeeded\n",
+ periph->periph_name, periph->unit_number));
+ }
+#endif
+
+ xpt_free_path(ccb->ccb_h.path);
+ free(ccb, M_USBDEV);
+}
+
+static void
+umass_cam_rescan(void *addr)
+{
+ struct umass_softc *sc = (struct umass_softc *) addr;
+ struct cam_path *path;
+ union ccb *ccb;
+
+ DPRINTF(UDMASS_SCSI, ("scbus%d: scanning for %s:%d:%d:%d\n",
+ cam_sim_path(sc->umass_sim),
+ device_get_nameunit(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ device_get_unit(sc->sc_dev), CAM_LUN_WILDCARD));
+
+ ccb = malloc(sizeof(union ccb), M_USBDEV, M_NOWAIT | M_ZERO);
+ if (ccb == NULL)
+ return;
+ if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->umass_sim),
+ device_get_unit(sc->sc_dev), CAM_LUN_WILDCARD)
+ != CAM_REQ_CMP) {
+ free(ccb, M_USBDEV);
+ return;
+ }
+
+ xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/);
+ ccb->ccb_h.func_code = XPT_SCAN_BUS;
+ ccb->ccb_h.cbfcnp = umass_cam_rescan_callback;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ xpt_action(ccb);
+
+ /* The scan is in progress now. */
+}
+
+static int
+umass_cam_attach(struct umass_softc *sc)
+{
+#ifndef USB_DEBUG
+ if (bootverbose)
+#endif
+ printf("%s:%d:%d:%d: Attached to scbus%d\n",
+ device_get_nameunit(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ device_get_unit(sc->sc_dev), CAM_LUN_WILDCARD,
+ cam_sim_path(sc->umass_sim));
+
+ if (!cold) {
+ /* Notify CAM of the new device after a short delay. Any
+ * failure is benign, as the user can still do it by hand
+ * (camcontrol rescan <busno>). Only do this if we are not
+ * booting, because CAM does a scan after booting has
+ * completed, when interrupts have been enabled.
+ */
+
+ callout_reset(&sc->cam_scsi_rescan_ch, MS_TO_TICKS(200),
+ umass_cam_rescan, sc);
+ }
+
+ return(0); /* always succesfull */
+}
+
+/* umass_cam_detach
+ * detach from the CAM layer
+ */
+
+static int
+umass_cam_detach_sim(struct umass_softc *sc)
+{
+ if (sc->umass_sim) {
+ if (xpt_bus_deregister(cam_sim_path(sc->umass_sim)))
+ cam_sim_free(sc->umass_sim, /*free_devq*/TRUE);
+ else
+ return(EBUSY);
+
+ sc->umass_sim = NULL;
+ }
+
+ return(0);
+}
+
+/* umass_cam_action
+ * CAM requests for action come through here
+ */
+
+static void
+umass_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct umass_softc *sc = (struct umass_softc *)sim->softc;
+
+ /* The softc is still there, but marked as going away. umass_cam_detach
+ * has not yet notified CAM of the lost device however.
+ */
+ if (sc && (sc->flags & UMASS_FLAGS_GONE)) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (gone)\n",
+ device_get_nameunit(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code));
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+
+ /* Verify, depending on the operation to perform, that we either got a
+ * valid sc, because an existing target was referenced, or otherwise
+ * the SIM is addressed.
+ *
+ * This avoids bombing out at a printf and does give the CAM layer some
+ * sensible feedback on errors.
+ */
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ case XPT_RESET_DEV:
+ case XPT_GET_TRAN_SETTINGS:
+ case XPT_SET_TRAN_SETTINGS:
+ case XPT_CALC_GEOMETRY:
+ /* the opcodes requiring a target. These should never occur. */
+ if (sc == NULL) {
+ printf("%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (target needed)\n",
+ DEVNAME_SIM, cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code);
+
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+ break;
+ case XPT_PATH_INQ:
+ case XPT_NOOP:
+ /* The opcodes sometimes aimed at a target (sc is valid),
+ * sometimes aimed at the SIM (sc is invalid and target is
+ * CAM_TARGET_WILDCARD)
+ */
+ if (sc == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (no wildcard)\n",
+ DEVNAME_SIM, cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code));
+
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+ break;
+ default:
+ /* XXX Hm, we should check the input parameters */
+ break;
+ }
+
+ /* Perform the requested action */
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ {
+ struct ccb_scsiio *csio = &ccb->csio; /* deref union */
+ int dir;
+ unsigned char *cmd;
+ int cmdlen;
+ unsigned char *rcmd;
+ int rcmdlen;
+
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SCSI_IO: "
+ "cmd: 0x%02x, flags: 0x%02x, "
+ "%db cmd/%db data/%db sense\n",
+ device_get_nameunit(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ csio->cdb_io.cdb_bytes[0],
+ ccb->ccb_h.flags & CAM_DIR_MASK,
+ csio->cdb_len, csio->dxfer_len,
+ csio->sense_len));
+
+ /* clear the end of the buffer to make sure we don't send out
+ * garbage.
+ */
+ DIF(UDMASS_SCSI, if ((ccb->ccb_h.flags & CAM_DIR_MASK)
+ == CAM_DIR_OUT)
+ umass_dump_buffer(sc, csio->data_ptr,
+ csio->dxfer_len, 48));
+
+ if (sc->transfer_state != TSTATE_IDLE) {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SCSI_IO: "
+ "I/O in progress, deferring (state %d, %s)\n",
+ device_get_nameunit(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ sc->transfer_state,states[sc->transfer_state]));
+ ccb->ccb_h.status = CAM_SCSI_BUSY;
+ xpt_done(ccb);
+ return;
+ }
+
+ switch(ccb->ccb_h.flags&CAM_DIR_MASK) {
+ case CAM_DIR_IN:
+ dir = DIR_IN;
+ break;
+ case CAM_DIR_OUT:
+ dir = DIR_OUT;
+ break;
+ default:
+ dir = DIR_NONE;
+ }
+
+ ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED;
+
+
+ if (csio->ccb_h.flags & CAM_CDB_POINTER) {
+ cmd = (unsigned char *) csio->cdb_io.cdb_ptr;
+ } else {
+ cmd = (unsigned char *) &csio->cdb_io.cdb_bytes;
+ }
+ cmdlen = csio->cdb_len;
+ rcmd = (unsigned char *) &sc->cam_scsi_command;
+ rcmdlen = sizeof(sc->cam_scsi_command);
+
+ /* sc->transform will convert the command to the command
+ * (format) needed by the specific command set and return
+ * the converted command in a buffer pointed to be rcmd.
+ * We pass in a buffer, but if the command does not
+ * have to be transformed it returns a ptr to the original
+ * buffer (see umass_scsi_transform).
+ */
+
+ switch (sc->transform(sc, cmd, cmdlen, &rcmd, &rcmdlen)) {
+ case 1:
+ /*
+ * Handle EVPD inquiry for broken devices first
+ * NO_INQUIRY also implies NO_INQUIRY_EVPD
+ */
+ if ((sc->quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) &&
+ rcmd[0] == INQUIRY && (rcmd[1] & SI_EVPD)) {
+ struct scsi_sense_data *sense;
+
+ sense = &ccb->csio.sense_data;
+ bzero(sense, sizeof(*sense));
+ sense->error_code = SSD_CURRENT_ERROR;
+ sense->flags = SSD_KEY_ILLEGAL_REQUEST;
+ sense->add_sense_code = 0x24;
+ sense->extra_len = 10;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
+ CAM_AUTOSNS_VALID;
+ xpt_done(ccb);
+ return;
+ }
+ /* Return fake inquiry data for broken devices */
+ if ((sc->quirks & NO_INQUIRY) && rcmd[0] == INQUIRY) {
+ struct ccb_scsiio *csio = &ccb->csio;
+
+ memcpy(csio->data_ptr, &fake_inq_data,
+ sizeof(fake_inq_data));
+ csio->scsi_status = SCSI_STATUS_OK;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+ }
+ if ((sc->quirks & NO_SYNCHRONIZE_CACHE) &&
+ rcmd[0] == SYNCHRONIZE_CACHE) {
+ struct ccb_scsiio *csio = &ccb->csio;
+
+ csio->scsi_status = SCSI_STATUS_OK;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+ }
+ if ((sc->quirks & FORCE_SHORT_INQUIRY) &&
+ rcmd[0] == INQUIRY) {
+ csio->dxfer_len = SHORT_INQUIRY_LENGTH;
+ }
+ sc->transfer(sc, ccb->ccb_h.target_lun, rcmd, rcmdlen,
+ csio->data_ptr,
+ csio->dxfer_len, dir, ccb->ccb_h.timeout,
+ umass_cam_cb, (void *) ccb);
+ break;
+ case 0:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ case 2:
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+
+ break;
+ }
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_PATH_INQ:.\n",
+ (sc == NULL? DEVNAME_SIM:device_get_nameunit(sc->sc_dev)),
+ cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ /* host specific information */
+ cpi->version_num = 1;
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NO_6_BYTE;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = UMASS_SCSIID_MAX; /* one target */
+ cpi->initiator_id = UMASS_SCSIID_HOST;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = device_get_unit(sc->sc_dev);
+ cpi->protocol = PROTO_SCSI;
+ cpi->protocol_version = SCSI_REV_2;
+ cpi->transport = XPORT_USB;
+ cpi->transport_version = 0;
+
+ if (sc == NULL) {
+ cpi->base_transfer_speed = 0;
+ cpi->max_lun = 0;
+ } else {
+ if (sc->quirks & FLOPPY_SPEED) {
+ cpi->base_transfer_speed =
+ UMASS_FLOPPY_TRANSFER_SPEED;
+ } else if (usbd_get_speed(sc->sc_udev) ==
+ USB_SPEED_HIGH) {
+ cpi->base_transfer_speed =
+ UMASS_HIGH_TRANSFER_SPEED;
+ } else {
+ cpi->base_transfer_speed =
+ UMASS_FULL_TRANSFER_SPEED;
+ }
+ cpi->max_lun = sc->maxlun;
+ }
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_RESET_DEV:
+ {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_RESET_DEV:.\n",
+ device_get_nameunit(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ umass_reset(sc, umass_cam_cb, (void *) ccb);
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+ cts->protocol = PROTO_SCSI;
+ cts->protocol_version = SCSI_REV_2;
+ cts->transport = XPORT_USB;
+ cts->transport_version = 0;
+ cts->xport_specific.valid = 0;
+
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_SET_TRAN_SETTINGS:.\n",
+ device_get_nameunit(sc->sc_dev), cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ cam_calc_geometry(&ccb->ccg, /*extended*/1);
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_NOOP:
+ {
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:XPT_NOOP:.\n",
+ (sc == NULL? DEVNAME_SIM:device_get_nameunit(sc->sc_dev)),
+ cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun));
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ DPRINTF(UDMASS_SCSI, ("%s:%d:%d:%d:func_code 0x%04x: "
+ "Not implemented\n",
+ (sc == NULL? DEVNAME_SIM:device_get_nameunit(sc->sc_dev)),
+ cam_sim_path(sc->umass_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code));
+
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+}
+
+static void
+umass_cam_poll(struct cam_sim *sim)
+{
+ struct umass_softc *sc = (struct umass_softc *) sim->softc;
+
+ DPRINTF(UDMASS_SCSI, ("%s: CAM poll\n",
+ device_get_nameunit(sc->sc_dev)));
+
+ usbd_set_polling(sc->sc_udev, 1);
+ usbd_dopoll(sc->iface);
+ usbd_set_polling(sc->sc_udev, 0);
+}
+
+
+/* umass_cam_cb
+ * finalise a completed CAM command
+ */
+
+static void
+umass_cam_cb(struct umass_softc *sc, void *priv, int residue, int status)
+{
+ union ccb *ccb = (union ccb *) priv;
+ struct ccb_scsiio *csio = &ccb->csio; /* deref union */
+
+ /* If the device is gone, just fail the request. */
+ if (sc->flags & UMASS_FLAGS_GONE) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+
+ csio->resid = residue;
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ if ((sc->quirks & READ_CAPACITY_OFFBY1) &&
+ (ccb->ccb_h.func_code == XPT_SCSI_IO) &&
+ (csio->cdb_io.cdb_bytes[0] == READ_CAPACITY)) {
+ struct scsi_read_capacity_data *rcap;
+ uint32_t maxsector;
+
+ rcap = (struct scsi_read_capacity_data *)csio->data_ptr;
+ maxsector = scsi_4btoul(rcap->addr) - 1;
+ scsi_ulto4b(maxsector, rcap->addr);
+ }
+ xpt_done(ccb);
+ break;
+
+ case STATUS_CMD_UNKNOWN:
+ case STATUS_CMD_FAILED:
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ {
+ unsigned char *rcmd;
+ int rcmdlen;
+
+ /* fetch sense data */
+ /* the rest of the command was filled in at attach */
+ sc->cam_scsi_sense.length = csio->sense_len;
+
+ DPRINTF(UDMASS_SCSI,("%s: Fetching %db sense data\n",
+ device_get_nameunit(sc->sc_dev), csio->sense_len));
+
+ rcmd = (unsigned char *) &sc->cam_scsi_command;
+ rcmdlen = sizeof(sc->cam_scsi_command);
+
+ if (sc->transform(sc,
+ (unsigned char *) &sc->cam_scsi_sense,
+ sizeof(sc->cam_scsi_sense),
+ &rcmd, &rcmdlen) == 1) {
+ if ((sc->quirks & FORCE_SHORT_INQUIRY) && (rcmd[0] == INQUIRY)) {
+ csio->sense_len = SHORT_INQUIRY_LENGTH;
+ }
+ sc->transfer(sc, ccb->ccb_h.target_lun,
+ rcmd, rcmdlen,
+ &csio->sense_data,
+ csio->sense_len, DIR_IN, ccb->ccb_h.timeout,
+ umass_cam_sense_cb, (void *) ccb);
+ } else {
+ panic("transform(REQUEST_SENSE) failed");
+ }
+ break;
+ }
+ case XPT_RESET_DEV: /* Reset failed */
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(ccb);
+ break;
+ default:
+ panic("umass_cam_cb called for func_code %d",
+ ccb->ccb_h.func_code);
+ }
+ break;
+
+ case STATUS_WIRE_FAILED:
+ /* the wire protocol failed and will have recovered
+ * (hopefully). We return an error to CAM and let CAM retry
+ * the command if necessary.
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(ccb);
+ break;
+ default:
+ panic("%s: Unknown status %d in umass_cam_cb",
+ device_get_nameunit(sc->sc_dev), status);
+ }
+}
+
+/* Finalise a completed autosense operation
+ */
+static void
+umass_cam_sense_cb(struct umass_softc *sc, void *priv, int residue, int status)
+{
+ union ccb *ccb = (union ccb *) priv;
+ struct ccb_scsiio *csio = &ccb->csio; /* deref union */
+ unsigned char *rcmd;
+ int rcmdlen;
+
+ if (sc->flags & UMASS_FLAGS_GONE) {
+ ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
+ xpt_done(ccb);
+ return;
+ }
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ case STATUS_CMD_UNKNOWN:
+ case STATUS_CMD_FAILED:
+ /* Getting sense data always succeeds (apart from wire
+ * failures).
+ */
+ if ((sc->quirks & RS_NO_CLEAR_UA)
+ && csio->cdb_io.cdb_bytes[0] == INQUIRY
+ && (csio->sense_data.flags & SSD_KEY)
+ == SSD_KEY_UNIT_ATTENTION) {
+ /* Ignore unit attention errors in the case where
+ * the Unit Attention state is not cleared on
+ * REQUEST SENSE. They will appear again at the next
+ * command.
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else if ((csio->sense_data.flags & SSD_KEY)
+ == SSD_KEY_NO_SENSE) {
+ /* No problem after all (in the case of CBI without
+ * CCI)
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else if ((sc->quirks & RS_NO_CLEAR_UA) &&
+ (csio->cdb_io.cdb_bytes[0] == READ_CAPACITY) &&
+ ((csio->sense_data.flags & SSD_KEY)
+ == SSD_KEY_UNIT_ATTENTION)) {
+ /*
+ * Some devices do not clear the unit attention error
+ * on request sense. We insert a test unit ready
+ * command to make sure we clear the unit attention
+ * condition, then allow the retry to proceed as
+ * usual.
+ */
+
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ csio->scsi_status = SCSI_STATUS_CHECK_COND;
+
+#if 0
+ DELAY(300000);
+#endif
+
+ DPRINTF(UDMASS_SCSI,("%s: Doing a sneaky"
+ "TEST_UNIT_READY\n",
+ device_get_nameunit(sc->sc_dev)));
+
+ /* the rest of the command was filled in at attach */
+
+ rcmd = (unsigned char *) &sc->cam_scsi_command2;
+ rcmdlen = sizeof(sc->cam_scsi_command2);
+
+ if (sc->transform(sc,
+ (unsigned char *)
+ &sc->cam_scsi_test_unit_ready,
+ sizeof(sc->cam_scsi_test_unit_ready),
+ &rcmd, &rcmdlen) == 1) {
+ sc->transfer(sc, ccb->ccb_h.target_lun,
+ rcmd, rcmdlen,
+ NULL, 0, DIR_NONE, ccb->ccb_h.timeout,
+ umass_cam_quirk_cb, (void *) ccb);
+ } else {
+ panic("transform(TEST_UNIT_READY) failed");
+ }
+ break;
+ } else {
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ csio->scsi_status = SCSI_STATUS_CHECK_COND;
+ }
+ xpt_done(ccb);
+ break;
+
+ default:
+ DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
+ device_get_nameunit(sc->sc_dev), status));
+ ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
+ xpt_done(ccb);
+ }
+}
+
+/*
+ * This completion code just handles the fact that we sent a test-unit-ready
+ * after having previously failed a READ CAPACITY with CHECK_COND. Even
+ * though this command succeeded, we have to tell CAM to retry.
+ */
+static void
+umass_cam_quirk_cb(struct umass_softc *sc, void *priv, int residue, int status)
+{
+ union ccb *ccb = (union ccb *) priv;
+
+ DPRINTF(UDMASS_SCSI, ("%s: Test unit ready returned status %d\n",
+ device_get_nameunit(sc->sc_dev), status));
+
+ if (sc->flags & UMASS_FLAGS_GONE) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+#if 0
+ ccb->ccb_h.status = CAM_REQ_CMP;
+#endif
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ xpt_done(ccb);
+}
+
+/*
+ * SCSI specific functions
+ */
+
+static int
+umass_scsi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ switch (cmd[0]) {
+ case TEST_UNIT_READY:
+ if (sc->quirks & NO_TEST_UNIT_READY) {
+ KASSERT(*rcmdlen >= sizeof(struct scsi_start_stop_unit),
+ ("rcmdlen = %d < %ld, buffer too small",
+ *rcmdlen,
+ (long)sizeof(struct scsi_start_stop_unit)));
+ DPRINTF(UDMASS_SCSI, ("%s: Converted TEST_UNIT_READY "
+ "to START_UNIT\n", device_get_nameunit(sc->sc_dev)));
+ memset(*rcmd, 0, *rcmdlen);
+ (*rcmd)[0] = START_STOP_UNIT;
+ (*rcmd)[4] = SSS_START;
+ return 1;
+ }
+ /* fallthrough */
+ case INQUIRY:
+ /* some drives wedge when asked for full inquiry information. */
+ if (sc->quirks & FORCE_SHORT_INQUIRY) {
+ memcpy(*rcmd, cmd, cmdlen);
+ *rcmdlen = cmdlen;
+ (*rcmd)[4] = SHORT_INQUIRY_LENGTH;
+ return 1;
+ }
+ /* fallthrough */
+ default:
+ *rcmd = cmd; /* We don't need to copy it */
+ *rcmdlen = cmdlen;
+ }
+
+ return 1;
+}
+/* RBC specific functions */
+static int
+umass_rbc_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ switch (cmd[0]) {
+ /* these commands are defined in RBC: */
+ case READ_10:
+ case READ_CAPACITY:
+ case START_STOP_UNIT:
+ case SYNCHRONIZE_CACHE:
+ case WRITE_10:
+ case 0x2f: /* VERIFY_10 is absent from scsi_all.h??? */
+ case INQUIRY:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case TEST_UNIT_READY:
+ case WRITE_BUFFER:
+ /* The following commands are not listed in my copy of the RBC specs.
+ * CAM however seems to want those, and at least the Sony DSC device
+ * appears to support those as well */
+ case REQUEST_SENSE:
+ case PREVENT_ALLOW:
+ if ((sc->quirks & RBC_PAD_TO_12) && cmdlen < 12) {
+ *rcmdlen = 12;
+ bcopy(cmd, *rcmd, cmdlen);
+ bzero(*rcmd + cmdlen, 12 - cmdlen);
+ } else {
+ *rcmd = cmd; /* We don't need to copy it */
+ *rcmdlen = cmdlen;
+ }
+ return 1;
+ /* All other commands are not legal in RBC */
+ default:
+ printf("%s: Unsupported RBC command 0x%02x",
+ device_get_nameunit(sc->sc_dev), cmd[0]);
+ printf("\n");
+ return 0; /* failure */
+ }
+}
+
+/*
+ * UFI specific functions
+ */
+static int
+umass_ufi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ /* A UFI command is always 12 bytes in length */
+ KASSERT(*rcmdlen >= UFI_COMMAND_LENGTH,
+ ("rcmdlen = %d < %d, buffer too small",
+ *rcmdlen, UFI_COMMAND_LENGTH));
+
+ *rcmdlen = UFI_COMMAND_LENGTH;
+ memset(*rcmd, 0, UFI_COMMAND_LENGTH);
+
+ switch (cmd[0]) {
+ /* Commands of which the format has been verified. They should work.
+ * Copy the command into the (zeroed out) destination buffer.
+ */
+ case TEST_UNIT_READY:
+ if (sc->quirks & NO_TEST_UNIT_READY) {
+ /* Some devices do not support this command.
+ * Start Stop Unit should give the same results
+ */
+ DPRINTF(UDMASS_UFI, ("%s: Converted TEST_UNIT_READY "
+ "to START_UNIT\n", device_get_nameunit(sc->sc_dev)));
+ (*rcmd)[0] = START_STOP_UNIT;
+ (*rcmd)[4] = SSS_START;
+ } else {
+ memcpy(*rcmd, cmd, cmdlen);
+ }
+ return 1;
+
+ case REZERO_UNIT:
+ case REQUEST_SENSE:
+ case FORMAT_UNIT:
+ case INQUIRY:
+ case START_STOP_UNIT:
+ case SEND_DIAGNOSTIC:
+ case PREVENT_ALLOW:
+ case READ_CAPACITY:
+ case READ_10:
+ case WRITE_10:
+ case POSITION_TO_ELEMENT: /* SEEK_10 */
+ case WRITE_AND_VERIFY:
+ case VERIFY:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case READ_12:
+ case WRITE_12:
+ case READ_FORMAT_CAPACITIES:
+ memcpy(*rcmd, cmd, cmdlen);
+ return 1;
+
+ /*
+ * SYNCHRONIZE_CACHE isn't supported by UFI, nor should it be
+ * required for UFI devices, so it is appropriate to fake
+ * success.
+ */
+ case SYNCHRONIZE_CACHE:
+ return 2;
+
+ default:
+ printf("%s: Unsupported UFI command 0x%02x\n",
+ device_get_nameunit(sc->sc_dev), cmd[0]);
+ return 0; /* failure */
+ }
+}
+
+/*
+ * 8070i (ATAPI) specific functions
+ */
+static int
+umass_atapi_transform(struct umass_softc *sc, unsigned char *cmd, int cmdlen,
+ unsigned char **rcmd, int *rcmdlen)
+{
+ /* An ATAPI command is always 12 bytes in length. */
+ KASSERT(*rcmdlen >= ATAPI_COMMAND_LENGTH,
+ ("rcmdlen = %d < %d, buffer too small",
+ *rcmdlen, ATAPI_COMMAND_LENGTH));
+
+ *rcmdlen = ATAPI_COMMAND_LENGTH;
+ memset(*rcmd, 0, ATAPI_COMMAND_LENGTH);
+
+ switch (cmd[0]) {
+ /* Commands of which the format has been verified. They should work.
+ * Copy the command into the (zeroed out) destination buffer.
+ */
+ case INQUIRY:
+ memcpy(*rcmd, cmd, cmdlen);
+ /* some drives wedge when asked for full inquiry information. */
+ if (sc->quirks & FORCE_SHORT_INQUIRY)
+ (*rcmd)[4] = SHORT_INQUIRY_LENGTH;
+ return 1;
+
+ case TEST_UNIT_READY:
+ if (sc->quirks & NO_TEST_UNIT_READY) {
+ KASSERT(*rcmdlen >= sizeof(struct scsi_start_stop_unit),
+ ("rcmdlen = %d < %ld, buffer too small",
+ *rcmdlen,
+ (long)sizeof(struct scsi_start_stop_unit)));
+ DPRINTF(UDMASS_SCSI, ("%s: Converted TEST_UNIT_READY "
+ "to START_UNIT\n", device_get_nameunit(sc->sc_dev)));
+ memset(*rcmd, 0, *rcmdlen);
+ (*rcmd)[0] = START_STOP_UNIT;
+ (*rcmd)[4] = SSS_START;
+ return 1;
+ }
+ /* fallthrough */
+ case REZERO_UNIT:
+ case REQUEST_SENSE:
+ case START_STOP_UNIT:
+ case SEND_DIAGNOSTIC:
+ case PREVENT_ALLOW:
+ case READ_CAPACITY:
+ case READ_10:
+ case WRITE_10:
+ case POSITION_TO_ELEMENT: /* SEEK_10 */
+ case SYNCHRONIZE_CACHE:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case READ_BUFFER:
+ case 0x42: /* READ_SUBCHANNEL */
+ case 0x43: /* READ_TOC */
+ case 0x44: /* READ_HEADER */
+ case 0x47: /* PLAY_MSF (Play Minute/Second/Frame) */
+ case 0x48: /* PLAY_TRACK */
+ case 0x49: /* PLAY_TRACK_REL */
+ case 0x4b: /* PAUSE */
+ case 0x51: /* READ_DISK_INFO */
+ case 0x52: /* READ_TRACK_INFO */
+ case 0x54: /* SEND_OPC */
+ case 0x59: /* READ_MASTER_CUE */
+ case 0x5b: /* CLOSE_TR_SESSION */
+ case 0x5c: /* READ_BUFFER_CAP */
+ case 0x5d: /* SEND_CUE_SHEET */
+ case 0xa1: /* BLANK */
+ case 0xa5: /* PLAY_12 */
+ case 0xa6: /* EXCHANGE_MEDIUM */
+ case 0xad: /* READ_DVD_STRUCTURE */
+ case 0xbb: /* SET_CD_SPEED */
+ case 0xe5: /* READ_TRACK_INFO_PHILIPS */
+ memcpy(*rcmd, cmd, cmdlen);
+ return 1;
+
+ case READ_12:
+ case WRITE_12:
+ default:
+ printf("%s: Unsupported ATAPI command 0x%02x"
+ " - trying anyway\n",
+ device_get_nameunit(sc->sc_dev), cmd[0]);
+ memcpy(*rcmd, cmd, cmdlen);
+ return 1;
+ }
+}
+
+
+/* (even the comment is missing) */
+
+DRIVER_MODULE(umass, uhub, umass_driver, umass_devclass, usbd_driver_load, 0);
+
+
+
+#ifdef USB_DEBUG
+static void
+umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw)
+{
+ int clen = cbw->bCDBLength;
+ int dlen = UGETDW(cbw->dCBWDataTransferLength);
+ u_int8_t *c = cbw->CBWCDB;
+ int tag = UGETDW(cbw->dCBWTag);
+ int flags = cbw->bCBWFlags;
+
+ DPRINTF(UDMASS_BBB, ("%s: CBW %d: cmd = %db "
+ "(0x%02x%02x%02x%02x%02x%02x%s), "
+ "data = %db, dir = %s\n",
+ device_get_nameunit(sc->sc_dev), tag, clen,
+ c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6? "...":""),
+ dlen, (flags == CBWFLAGS_IN? "in":
+ (flags == CBWFLAGS_OUT? "out":"<invalid>"))));
+}
+
+static void
+umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw)
+{
+ int sig = UGETDW(csw->dCSWSignature);
+ int tag = UGETW(csw->dCSWTag);
+ int res = UGETDW(csw->dCSWDataResidue);
+ int status = csw->bCSWStatus;
+
+ DPRINTF(UDMASS_BBB, ("%s: CSW %d: sig = 0x%08x (%s), tag = %d, "
+ "res = %d, status = 0x%02x (%s)\n", device_get_nameunit(sc->sc_dev),
+ tag, sig, (sig == CSWSIGNATURE? "valid":"invalid"),
+ tag, res,
+ status, (status == CSWSTATUS_GOOD? "good":
+ (status == CSWSTATUS_FAILED? "failed":
+ (status == CSWSTATUS_PHASE? "phase":"<invalid>")))));
+}
+
+static void
+umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, int cmdlen)
+{
+ u_int8_t *c = cmd;
+ int dir = sc->transfer_dir;
+
+ DPRINTF(UDMASS_BBB, ("%s: cmd = %db "
+ "(0x%02x%02x%02x%02x%02x%02x%s), "
+ "data = %db, dir = %s\n",
+ device_get_nameunit(sc->sc_dev), cmdlen,
+ c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6? "...":""),
+ sc->transfer_datalen,
+ (dir == DIR_IN? "in":
+ (dir == DIR_OUT? "out":
+ (dir == DIR_NONE? "no data phase": "<invalid>")))));
+}
+
+static void
+umass_dump_buffer(struct umass_softc *sc, u_int8_t *buffer, int buflen,
+ int printlen)
+{
+ int i, j;
+ char s1[40];
+ char s2[40];
+ char s3[5];
+
+ s1[0] = '\0';
+ s3[0] = '\0';
+
+ sprintf(s2, " buffer=%p, buflen=%d", buffer, buflen);
+ for (i = 0; i < buflen && i < printlen; i++) {
+ j = i % 16;
+ if (j == 0 && i != 0) {
+ DPRINTF(UDMASS_GEN, ("%s: 0x %s%s\n",
+ device_get_nameunit(sc->sc_dev), s1, s2));
+ s2[0] = '\0';
+ }
+ sprintf(&s1[j*2], "%02x", buffer[i] & 0xff);
+ }
+ if (buflen > printlen)
+ sprintf(s3, " ...");
+ DPRINTF(UDMASS_GEN, ("%s: 0x %s%s%s\n",
+ device_get_nameunit(sc->sc_dev), s1, s2, s3));
+}
+#endif
diff --git a/sys/legacy/dev/usb/umct.c b/sys/legacy/dev/usb/umct.c
new file mode 100644
index 0000000..63de146
--- /dev/null
+++ b/sys/legacy/dev/usb/umct.c
@@ -0,0 +1,511 @@
+/*-
+ * Copyright (c) 2003 Scott Long
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the MCT (Magic Control Technology) USB-RS232 Converter.
+ * Based on the superb documentation from the linux mct_u232 driver by
+ * Wolfgang Grandeggar <wolfgang@cec.ch>.
+ * This device smells a lot like the Belkin F5U103, except that it has
+ * suffered some mild brain-damage. This driver is based off of the ubsa.c
+ * driver from Alexander Kabaev <kan@freebsd.org>. Merging the two together
+ * might be useful, though the subtle differences might lead to lots of
+ * #ifdef's.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/tty.h>
+#include <sys/taskqueue.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/ucomvar.h>
+
+/* The UMCT advertises the standard 8250 UART registers */
+#define UMCT_GET_MSR 2 /* Get Modem Status Register */
+#define UMCT_GET_MSR_SIZE 1
+#define UMCT_GET_LCR 6 /* Get Line Control Register */
+#define UMCT_GET_LCR_SIZE 1
+#define UMCT_SET_BAUD 5 /* Set the Baud Rate Divisor */
+#define UMCT_SET_BAUD_SIZE 4
+#define UMCT_SET_LCR 7 /* Set Line Control Register */
+#define UMCT_SET_LCR_SIZE 1
+#define UMCT_SET_MCR 10 /* Set Modem Control Register */
+#define UMCT_SET_MCR_SIZE 1
+
+#define UMCT_INTR_INTERVAL 100
+#define UMCT_IFACE_INDEX 0
+#define UMCT_CONFIG_INDEX 1
+
+struct umct_softc {
+ struct ucom_softc sc_ucom;
+ int sc_iface_number;
+ usbd_interface_handle sc_intr_iface;
+ int sc_intr_number;
+ usbd_pipe_handle sc_intr_pipe;
+ u_char *sc_intr_buf;
+ int sc_isize;
+ uint8_t sc_lsr;
+ uint8_t sc_msr;
+ uint8_t sc_lcr;
+ uint8_t sc_mcr;
+ struct task sc_task;
+};
+
+static void umct_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void umct_get_status(void *, int, u_char *, u_char *);
+static void umct_set(void *, int, int, int);
+static int umct_param(void *, int, struct termios *);
+static int umct_open(void *, int);
+static void umct_close(void *, int);
+static void umct_notify(void *, int count);
+
+static struct ucom_callback umct_callback = {
+ umct_get_status, /* ucom_get_status */
+ umct_set, /* ucom_set */
+ umct_param, /* ucom_param */
+ NULL, /* ucom_ioctl */
+ umct_open, /* ucom_open */
+ umct_close, /* ucom_close */
+ NULL, /* ucom_read */
+ NULL /* ucom_write */
+};
+
+static const struct umct_product {
+ uint16_t vendor;
+ uint16_t product;
+} umct_products[] = {
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232 },
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232 },
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232 },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109 },
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409 },
+ { 0, 0 }
+};
+
+static device_probe_t umct_match;
+static device_attach_t umct_attach;
+static device_detach_t umct_detach;
+
+static device_method_t umct_methods[] = {
+ DEVMETHOD(device_probe, umct_match),
+ DEVMETHOD(device_attach, umct_attach),
+ DEVMETHOD(device_detach, umct_detach),
+ { 0, 0 }
+};
+
+static driver_t umct_driver = {
+ "ucom",
+ umct_methods,
+ sizeof(struct umct_softc)
+};
+
+DRIVER_MODULE(umct, uhub, umct_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(umct, usb, 1, 1, 1);
+MODULE_DEPEND(umct, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(umct, 1);
+
+static int
+umct_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ int i;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ for (i = 0; umct_products[i].vendor != 0; i++) {
+ if (umct_products[i].vendor == uaa->vendor &&
+ umct_products[i].product == uaa->product) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ }
+
+ return (UMATCH_NONE);
+}
+
+static int
+umct_attach(device_t self)
+{
+ struct umct_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ const char *devname;
+ usbd_status err;
+ int i;
+
+ dev = uaa->device;
+ bzero(sc, sizeof(struct umct_softc));
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ ucom->sc_bulkout_no = -1;
+ ucom->sc_bulkin_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ devname = device_get_nameunit(ucom->sc_dev);
+
+ err = usbd_set_config_index(dev, UMCT_CONFIG_INDEX, 1);
+ if (err) {
+ printf("%s: failed to set configuration: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ err = usbd_device2interface_handle(dev, UMCT_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get interface: %s\n", devname,
+ usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ devname, i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /*
+ * The real bulk-in endpoint is also marked as an interrupt.
+ * The only way to differentiate it from the real interrupt
+ * endpoint is to look at the wMaxPacketSize field.
+ */
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
+ if (UGETW(ed->wMaxPacketSize) == 0x2) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ } else {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ ucom->sc_ibufsize = UGETW(ed->wMaxPacketSize);
+ }
+ continue;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ if (uaa->product == USB_PRODUCT_MCT_SITECOM_USB232)
+ ucom->sc_obufsize = 16; /* device is broken */
+ else
+ ucom->sc_obufsize = UGETW(ed->wMaxPacketSize);
+ continue;
+ }
+
+ printf("%s: warning - unsupported endpoint 0x%x\n", devname,
+ ed->bEndpointAddress);
+ }
+
+ if (sc->sc_intr_number == -1) {
+ printf("%s: Could not find interrupt in\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ sc->sc_intr_iface = ucom->sc_iface;
+
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &umct_callback;
+ ucom_attach(ucom);
+ TASK_INIT(&sc->sc_task, 0, umct_notify, sc);
+ return 0;
+
+error:
+ return ENXIO;
+}
+
+static int
+umct_detach(device_t self)
+{
+ struct umct_softc *sc = device_get_softc(self);
+
+ int rv;
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+#if 0
+ taskqueue_drain(taskqueue_swi_giant);
+#endif
+ rv = ucom_detach(&sc->sc_ucom);
+ return (rv);
+}
+
+static int
+umct_request(struct umct_softc *sc, uint8_t request, int len, uint32_t value)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ uint8_t oval[4];
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = request;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, len);
+ USETDW(oval, value);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, oval);
+ if (err)
+ printf("%s: umct_request: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+}
+
+static void
+umct_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct umct_softc *sc;
+ u_char *buf;
+
+ sc = (struct umct_softc *)priv;
+ buf = sc->sc_intr_buf;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ sc->sc_msr = buf[0];
+ sc->sc_lsr = buf[1];
+
+ /*
+ * Defer notifying the ucom layer as it doesn't like to be bothered
+ * from an interrupt context.
+ */
+ taskqueue_enqueue(taskqueue_swi_giant, &sc->sc_task);
+}
+
+static void
+umct_notify(void *arg, int count)
+{
+ struct umct_softc *sc;
+
+ sc = (struct umct_softc *)arg;
+ if (sc->sc_ucom.sc_dying == 0)
+ ucom_status_change(&sc->sc_ucom);
+}
+
+static void
+umct_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct umct_softc *sc;
+
+ sc = addr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+
+ return;
+}
+
+static void
+umct_set(void *addr, int portno, int reg, int onoff)
+{
+ struct umct_softc *sc;
+
+ sc = addr;
+ switch (reg) {
+ case UCOM_SET_BREAK:
+ sc->sc_lcr &= ~0x40;
+ sc->sc_lcr |= (onoff) ? 0x40 : 0;
+ umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr);
+ break;
+ case UCOM_SET_DTR:
+ sc->sc_mcr &= ~0x01;
+ sc->sc_mcr |= (onoff) ? 0x01 : 0;
+ umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+ break;
+ case UCOM_SET_RTS:
+ sc->sc_mcr &= ~0x2;
+ sc->sc_mcr |= (onoff) ? 0x02 : 0;
+ umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+umct_calc_baud(u_int baud)
+{
+ switch(baud) {
+ case B300: return (0x1);
+ case B600: return (0x2);
+ case B1200: return (0x3);
+ case B2400: return (0x4);
+ case B4800: return (0x6);
+ case B9600: return (0x8);
+ case B19200: return (0x9);
+ case B38400: return (0xa);
+ case B57600: return (0xb);
+ case 115200: return (0xc);
+ case B0:
+ default:
+ break;
+ }
+
+ return (0x0);
+}
+
+static int
+umct_param(void *addr, int portno, struct termios *ti)
+{
+ struct umct_softc *sc;
+ uint32_t value;
+
+ sc = addr;
+ value = umct_calc_baud(ti->c_ospeed);
+ umct_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value);
+
+ value = sc->sc_lcr & 0x40;
+
+ switch (ti->c_cflag & CSIZE) {
+ case CS5: value |= 0x0; break;
+ case CS6: value |= 0x1; break;
+ case CS7: value |= 0x2; break;
+ case CS8: value |= 0x3; break;
+ default: value |= 0x0; break;
+ }
+
+ value |= (ti->c_cflag & CSTOPB) ? 0x4 : 0;
+ if (ti->c_cflag & PARENB) {
+ value |= 0x8;
+ value |= (ti->c_cflag & PARODD) ? 0x0 : 0x10;
+ }
+
+ /*
+ * XXX There doesn't seem to be a way to tell the device to use flow
+ * control.
+ */
+
+ sc->sc_lcr = value;
+ umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value);
+
+ return (0);
+}
+
+static int
+umct_open(void *addr, int portno)
+{
+ struct umct_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying) {
+ return (ENXIO);
+ }
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number,
+ USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buf,
+ sc->sc_isize, umct_intr, UMCT_INTR_INTERVAL);
+ if (err) {
+ printf("%s: cannot open interrupt pipe (addr %d)\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev),
+ sc->sc_intr_number);
+ free(sc->sc_intr_buf, M_USBDEV);
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+static void
+umct_close(void *addr, int portno)
+{
+ struct umct_softc *sc;
+ int err;
+
+ sc = addr;
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: abort interrupt pipe failed: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: close interrupt pipe failed: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
diff --git a/sys/legacy/dev/usb/umodem.c b/sys/legacy/dev/usb/umodem.c
new file mode 100644
index 0000000..f0cb17c
--- /dev/null
+++ b/sys/legacy/dev/usb/umodem.c
@@ -0,0 +1,821 @@
+/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*-
+ * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*-
+ * 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.
+ */
+
+/*
+ * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+/*
+ * TODO:
+ * - Add error recovery in various places; the big problem is what
+ * to do in a callback if there is an error.
+ * - Implement a Call Device for modems without multiplexed commands.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/serial.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+#include <sys/taskqueue.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+#include "usbdevs.h"
+
+#ifdef USB_DEBUG
+int umodemdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
+SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW,
+ &umodemdebug, 0, "umodem debug level");
+#define DPRINTFN(n, x) if (umodemdebug > (n)) printf x
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+static const struct umodem_product {
+ u_int16_t vendor;
+ u_int16_t product;
+ u_int8_t interface;
+} umodem_products[] = {
+ /* Kyocera AH-K3001V*/
+ { USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 0 },
+ { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 0 },
+ { USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 0 },
+ { 0, 0, 0 },
+};
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * As speeds for umodem deivces increase, these numbers will need to
+ * be increased. They should be good for G3 speeds and below.
+ */
+#define UMODEMIBUFSIZE 1024
+#define UMODEMOBUFSIZE 1024
+
+#define UMODEM_MODVER 1 /* module version */
+
+struct umodem_softc {
+ struct ucom_softc sc_ucom;
+
+ device_t sc_dev; /* base device */
+
+ usbd_device_handle sc_udev; /* USB device */
+
+ int sc_ctl_iface_no;
+ usbd_interface_handle sc_ctl_iface; /* control interface */
+ int sc_data_iface_no;
+ usbd_interface_handle sc_data_iface; /* data interface */
+
+ int sc_cm_cap; /* CM capabilities */
+ int sc_acm_cap; /* ACM capabilities */
+
+ int sc_cm_over_data;
+
+ usb_cdc_line_state_t sc_line_state; /* current line state */
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+
+ u_char sc_opening; /* lock during open */
+
+ int sc_ctl_notify; /* Notification endpoint */
+ usbd_pipe_handle sc_notify_pipe; /* Notification pipe */
+ usb_cdc_notification_t sc_notify_buf; /* Notification structure */
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* Modem status register */
+
+ struct task sc_task;
+};
+
+static void *umodem_get_desc(usbd_device_handle dev, int type, int subtype);
+static usbd_status umodem_set_comm_feature(struct umodem_softc *sc,
+ int feature, int state);
+static usbd_status umodem_set_line_coding(struct umodem_softc *sc,
+ usb_cdc_line_state_t *state);
+
+static void umodem_get_caps(usbd_device_handle, int *, int *);
+
+static void umodem_get_status(void *, int portno, u_char *lsr, u_char *msr);
+static void umodem_set(void *, int, int, int);
+static void umodem_dtr(struct umodem_softc *, int);
+static void umodem_rts(struct umodem_softc *, int);
+static void umodem_break(struct umodem_softc *, int);
+static void umodem_set_line_state(struct umodem_softc *);
+static int umodem_param(void *, int, struct termios *);
+static int umodem_ioctl(void *, int, u_long, caddr_t, struct thread *);
+static int umodem_open(void *, int portno);
+static void umodem_close(void *, int portno);
+static void umodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void umodem_notify(void *, int);
+
+static struct ucom_callback umodem_callback = {
+ .ucom_get_status = umodem_get_status,
+ .ucom_set = umodem_set,
+ .ucom_param = umodem_param,
+ .ucom_ioctl = umodem_ioctl,
+ .ucom_open = umodem_open,
+ .ucom_close = umodem_close
+};
+
+static device_probe_t umodem_match;
+static device_attach_t umodem_attach;
+static device_detach_t umodem_detach;
+
+static device_method_t umodem_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, umodem_match),
+ DEVMETHOD(device_attach, umodem_attach),
+ DEVMETHOD(device_detach, umodem_detach),
+ { 0, 0 }
+};
+
+static driver_t umodem_driver = {
+ "ucom",
+ umodem_methods,
+ sizeof (struct umodem_softc)
+};
+
+DRIVER_MODULE(umodem, uhub, umodem_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(umodem, usb, 1, 1, 1);
+MODULE_DEPEND(umodem, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(umodem, UMODEM_MODVER);
+
+static int
+umodem_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+ usb_device_descriptor_t *dd;
+ int cm, acm, i, ret;
+
+ if (uaa->iface == NULL)
+ return (UMATCH_NONE);
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ dd = usbd_get_device_descriptor(uaa->device);
+ if (id == NULL || dd == NULL)
+ return (UMATCH_NONE);
+
+ ret = UMATCH_NONE;
+ for (i = 0; umodem_products[i].vendor != 0; i++) {
+ if (umodem_products[i].vendor == UGETW(dd->idVendor) &&
+ umodem_products[i].product == UGETW(dd->idProduct) &&
+ umodem_products[i].interface == id->bInterfaceNumber) {
+ ret = UMATCH_VENDOR_PRODUCT;
+ break;
+ }
+ }
+
+ if (ret == UMATCH_NONE &&
+ id->bInterfaceClass == UICLASS_CDC &&
+ id->bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL &&
+ id->bInterfaceProtocol == UIPROTO_CDC_AT)
+ ret = UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO;
+
+ if (ret == UMATCH_NONE)
+ return (ret);
+
+ umodem_get_caps(uaa->device, &cm, &acm);
+ if (!(cm & USB_CDC_CM_DOES_CM) ||
+ !(cm & USB_CDC_CM_OVER_DATA) ||
+ !(acm & USB_CDC_ACM_HAS_LINE))
+ return (UMATCH_NONE);
+
+ return ret;
+}
+
+static int
+umodem_attach(device_t self)
+{
+ struct umodem_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usb_cdc_cm_descriptor_t *cmd;
+ int data_ifcno;
+ int i;
+ struct ucom_softc *ucom;
+
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ sc->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ sc->sc_udev = dev;
+ sc->sc_ctl_iface = uaa->iface;
+ id = usbd_get_interface_descriptor(sc->sc_ctl_iface);
+ sc->sc_ctl_iface_no = id->bInterfaceNumber;
+ device_printf(self, "iclass %d/%d\n", id->bInterfaceClass,
+ id->bInterfaceSubClass);
+
+ umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap);
+
+ /* Get the data interface no. */
+ cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+ if (cmd == NULL) {
+ device_printf(sc->sc_dev, "no CM descriptor\n");
+ goto bad;
+ }
+ sc->sc_data_iface_no = data_ifcno = cmd->bDataInterface;
+
+ device_printf(sc->sc_dev,
+ "data interface %d, has %sCM over data, has %sbreak\n",
+ data_ifcno, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+ sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+ /* Get the data interface too. */
+ for (i = 0; i < uaa->nifaces; i++) {
+ if (uaa->ifaces[i] != NULL) {
+ id = usbd_get_interface_descriptor(uaa->ifaces[i]);
+ if (id != NULL && id->bInterfaceNumber == data_ifcno) {
+ sc->sc_data_iface = uaa->ifaces[i];
+ uaa->ifaces[i] = NULL;
+ }
+ }
+ }
+ if (sc->sc_data_iface == NULL) {
+ device_printf(sc->sc_dev, "no data interface\n");
+ goto bad;
+ }
+ ucom->sc_iface = sc->sc_data_iface;
+
+ /*
+ * Find the bulk endpoints.
+ * Iterate over all endpoints in the data interface and take note.
+ */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+
+ id = usbd_get_interface_descriptor(sc->sc_data_iface);
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
+ if (ed == NULL) {
+ device_printf(sc->sc_dev,
+ "no endpoint descriptor for %d\n", i);
+ goto bad;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+
+ if (ucom->sc_bulkin_no == -1) {
+ device_printf(sc->sc_dev, "Could not find data bulk in\n");
+ goto bad;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ device_printf(sc->sc_dev, "Could not find data bulk out\n");
+ goto bad;
+ }
+
+ if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
+ if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE)
+ umodem_set_comm_feature(sc, UCDC_ABSTRACT_STATE,
+ UCDC_DATA_MULTIPLEXED);
+ sc->sc_cm_over_data = 1;
+ }
+
+ /*
+ * The standard allows for notification messages (to indicate things
+ * like a modem hangup) to come in via an interrupt endpoint
+ * off of the control interface. Iterate over the endpoints on
+ * the control interface and see if there are any interrupt
+ * endpoints; if there are, then register it.
+ */
+
+ sc->sc_ctl_notify = -1;
+ sc->sc_notify_pipe = NULL;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i);
+ if (ed == NULL)
+ continue;
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
+ device_printf(sc->sc_dev,
+ "status change notification available\n");
+ sc->sc_ctl_notify = ed->bEndpointAddress;
+ }
+ }
+
+ sc->sc_dtr = -1;
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UMODEMIBUFSIZE;
+ ucom->sc_obufsize = UMODEMOBUFSIZE;
+ ucom->sc_ibufsizepad = UMODEMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &umodem_callback;
+
+ TASK_INIT(&sc->sc_task, 0, umodem_notify, sc);
+ ucom_attach(&sc->sc_ucom);
+ return 0;
+
+ bad:
+ ucom->sc_dying = 1;
+ return ENXIO;
+}
+
+static int
+umodem_open(void *addr, int portno)
+{
+ struct umodem_softc *sc = addr;
+ int err;
+
+ DPRINTF(("umodem_open: sc=%p\n", sc));
+
+ if (sc->sc_ctl_notify != -1 && sc->sc_notify_pipe == NULL) {
+ err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_ctl_notify,
+ USBD_SHORT_XFER_OK, &sc->sc_notify_pipe, sc,
+ &sc->sc_notify_buf, sizeof(sc->sc_notify_buf),
+ umodem_intr, USBD_DEFAULT_INTERVAL);
+
+ if (err) {
+ DPRINTF(("Failed to establish notify pipe: %s\n",
+ usbd_errstr(err)));
+ return EIO;
+ }
+ }
+
+ return 0;
+}
+
+static void
+umodem_close(void *addr, int portno)
+{
+ struct umodem_softc *sc = addr;
+ int err;
+
+ DPRINTF(("umodem_close: sc=%p\n", sc));
+
+ if (sc->sc_notify_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_notify_pipe);
+ if (err)
+ device_printf(sc->sc_dev,
+ "abort notify pipe failed: %s\n",
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_notify_pipe);
+ if (err)
+ device_printf(sc->sc_dev,
+ "close notify pipe failed: %s\n",
+ usbd_errstr(err));
+ sc->sc_notify_pipe = NULL;
+ }
+}
+
+static void
+umodem_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct umodem_softc *sc = priv;
+ u_char mstatus;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ device_printf(sc->sc_dev, "abnormal status: %s\n",
+ usbd_errstr(status));
+ return;
+ }
+
+ if (sc->sc_notify_buf.bmRequestType != UCDC_NOTIFICATION) {
+ DPRINTF(("%s: unknown message type (%02x) on notify pipe\n",
+ device_get_nameunit(sc->sc_dev),
+ sc->sc_notify_buf.bmRequestType));
+ return;
+ }
+
+ switch (sc->sc_notify_buf.bNotification) {
+ case UCDC_N_SERIAL_STATE:
+ /*
+ * Set the serial state in ucom driver based on
+ * the bits from the notify message
+ */
+ if (UGETW(sc->sc_notify_buf.wLength) != 2) {
+ device_printf(sc->sc_dev,
+ "Invalid notification length! (%d)\n",
+ UGETW(sc->sc_notify_buf.wLength));
+ break;
+ }
+ DPRINTF(("%s: notify bytes = %02x%02x\n",
+ device_get_nameunit(sc->sc_dev),
+ sc->sc_notify_buf.data[0],
+ sc->sc_notify_buf.data[1]));
+ /* Currently, lsr is always zero. */
+ sc->sc_lsr = sc->sc_msr = 0;
+ mstatus = sc->sc_notify_buf.data[0];
+
+ if (ISSET(mstatus, UCDC_N_SERIAL_RI))
+ sc->sc_msr |= SER_RI;
+ if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
+ sc->sc_msr |= SER_DSR;
+ if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
+ sc->sc_msr |= SER_DCD;
+ /* Deferred notifying to the ucom layer */
+ taskqueue_enqueue(taskqueue_swi_giant, &sc->sc_task);
+ break;
+ default:
+ DPRINTF(("%s: unknown notify message: %02x\n",
+ device_get_nameunit(sc->sc_dev),
+ sc->sc_notify_buf.bNotification));
+ break;
+ }
+}
+
+static void
+umodem_notify(void *arg, int count)
+{
+ struct umodem_softc *sc;
+
+ sc = (struct umodem_softc *)arg;
+ if (sc->sc_ucom.sc_dying)
+ return;
+ ucom_status_change(&sc->sc_ucom);
+}
+
+void
+umodem_get_caps(usbd_device_handle dev, int *cm, int *acm)
+{
+ usb_cdc_cm_descriptor_t *cmd;
+ usb_cdc_acm_descriptor_t *cad;
+
+ *cm = *acm = 0;
+
+ cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+ if (cmd == NULL) {
+ DPRINTF(("umodem_get_desc: no CM desc\n"));
+ return;
+ }
+ *cm = cmd->bmCapabilities;
+
+ cad = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+ if (cad == NULL) {
+ DPRINTF(("umodem_get_desc: no ACM desc\n"));
+ return;
+ }
+ *acm = cad->bmCapabilities;
+}
+
+void
+umodem_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct umodem_softc *sc = addr;
+
+ DPRINTF(("umodem_get_status:\n"));
+
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
+
+int
+umodem_param(void *addr, int portno, struct termios *t)
+{
+ struct umodem_softc *sc = addr;
+ usbd_status err;
+ usb_cdc_line_state_t ls;
+
+ DPRINTF(("umodem_param: sc=%p\n", sc));
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+ if (ISSET(t->c_cflag, CSTOPB))
+ ls.bCharFormat = UCDC_STOP_BIT_2;
+ else
+ ls.bCharFormat = UCDC_STOP_BIT_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ ls.bParityType = UCDC_PARITY_ODD;
+ else
+ ls.bParityType = UCDC_PARITY_EVEN;
+ } else
+ ls.bParityType = UCDC_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ err = umodem_set_line_coding(sc, &ls);
+ if (err) {
+ DPRINTF(("umodem_param: err=%s\n", usbd_errstr(err)));
+ return (EIO);
+ }
+ return (0);
+}
+
+int
+umodem_ioctl(void *addr, int portno, u_long cmd, caddr_t data,
+ struct thread *p)
+{
+ struct umodem_softc *sc = addr;
+ int error = 0;
+
+ if (sc->sc_ucom.sc_dying)
+ return (EIO);
+
+ DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd));
+
+ switch (cmd) {
+ case USB_GET_CM_OVER_DATA:
+ *(int *)data = sc->sc_cm_over_data;
+ break;
+
+ case USB_SET_CM_OVER_DATA:
+ if (*(int *)data != sc->sc_cm_over_data) {
+ /* XXX change it */
+ }
+ break;
+
+ default:
+ DPRINTF(("umodemioctl: unknown\n"));
+ error = ENOIOCTL;
+ break;
+ }
+
+ return (error);
+}
+
+void
+umodem_dtr(struct umodem_softc *sc, int onoff)
+{
+ DPRINTF(("umodem_modem: onoff=%d\n", onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+ sc->sc_dtr = onoff;
+
+ umodem_set_line_state(sc);
+}
+
+void
+umodem_rts(struct umodem_softc *sc, int onoff)
+{
+ DPRINTF(("umodem_modem: onoff=%d\n", onoff));
+
+ if (sc->sc_rts == onoff)
+ return;
+ sc->sc_rts = onoff;
+
+ umodem_set_line_state(sc);
+}
+
+void
+umodem_set_line_state(struct umodem_softc *sc)
+{
+ usb_device_request_t req;
+ int ls;
+
+ ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
+ (sc->sc_rts ? UCDC_LINE_RTS : 0);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, ls);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ (void)usbd_do_request(sc->sc_udev, &req, 0);
+
+}
+
+void
+umodem_break(struct umodem_softc *sc, int onoff)
+{
+ usb_device_request_t req;
+
+ DPRINTF(("umodem_break: onoff=%d\n", onoff));
+
+ if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK))
+ return;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, 0);
+
+ (void)usbd_do_request(sc->sc_udev, &req, 0);
+}
+
+void
+umodem_set(void *addr, int portno, int reg, int onoff)
+{
+ struct umodem_softc *sc = addr;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ umodem_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ umodem_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ umodem_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+usbd_status
+umodem_set_line_coding(struct umodem_softc *sc, usb_cdc_line_state_t *state)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("umodem_set_line_coding: rate=%d fmt=%d parity=%d bits=%d\n",
+ UGETDW(state->dwDTERate), state->bCharFormat,
+ state->bParityType, state->bDataBits));
+
+ if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
+ DPRINTF(("umodem_set_line_coding: already set\n"));
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+ err = usbd_do_request(sc->sc_udev, &req, state);
+ if (err) {
+ DPRINTF(("umodem_set_line_coding: failed, err=%s\n",
+ usbd_errstr(err)));
+ return (err);
+ }
+
+ sc->sc_line_state = *state;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void *
+umodem_get_desc(usbd_device_handle dev, int type, int subtype)
+{
+ usb_descriptor_t *desc;
+ usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+ uByte *p = (uByte *)cd;
+ uByte *end = p + UGETW(cd->wTotalLength);
+
+ while (p < end) {
+ desc = (usb_descriptor_t *)p;
+ if (desc->bDescriptorType == type &&
+ desc->bDescriptorSubtype == subtype)
+ return (desc);
+ p += desc->bLength;
+ }
+
+ return (0);
+}
+
+usbd_status
+umodem_set_comm_feature(struct umodem_softc *sc, int feature, int state)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ usb_cdc_abstract_state_t ast;
+
+ DPRINTF(("umodem_set_comm_feature: feature=%d state=%d\n", feature,
+ state));
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_COMM_FEATURE;
+ USETW(req.wValue, feature);
+ USETW(req.wIndex, sc->sc_ctl_iface_no);
+ USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
+ USETW(ast.wState, state);
+
+ err = usbd_do_request(sc->sc_udev, &req, &ast);
+ if (err) {
+ DPRINTF(("umodem_set_comm_feature: feature=%d, err=%s\n",
+ feature, usbd_errstr(err)));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static int
+umodem_detach(device_t self)
+{
+ struct umodem_softc *sc = device_get_softc(self);
+ int rv = 0;
+
+ DPRINTF(("umodem_detach: sc=%p\n", sc));
+
+ if (sc->sc_notify_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_notify_pipe);
+ usbd_close_pipe(sc->sc_notify_pipe);
+ sc->sc_notify_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
diff --git a/sys/legacy/dev/usb/ums.c b/sys/legacy/dev/usb/ums.c
new file mode 100644
index 0000000..6484303
--- /dev/null
+++ b/sys/legacy/dev/usb/ums.c
@@ -0,0 +1,972 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+#include <dev/usb/hid.h>
+
+#include <sys/mouse.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (umsdebug) printf x
+#define DPRINTFN(n,x) if (umsdebug>(n)) printf x
+int umsdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
+SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
+ &umsdebug, 0, "ums debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UMSUNIT(s) (dev2unit(s)&0x1f)
+
+#define MS_TO_TICKS(ms) ((ms) * hz / 1000)
+
+#define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */
+
+struct ums_softc {
+ device_t sc_dev; /* base device */
+ usbd_interface_handle sc_iface; /* interface */
+ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
+ int sc_ep_addr;
+
+ u_char *sc_ibuf;
+ u_int8_t sc_iid;
+ int sc_isize;
+ struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_t, sc_loc_w;
+ struct hid_location *sc_loc_btn;
+
+ struct callout callout_handle; /* for spurious button ups */
+
+ int sc_enabled;
+ int sc_disconnected; /* device is gone */
+
+ int flags; /* device configuration */
+#define UMS_Z 0x01 /* z direction available */
+#define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */
+#define UMS_T 0x04 /* aa direction available (tilt) */
+#define UMS_REVZ 0x08 /* Z-axis is reversed */
+ int nbuttons;
+#define MAX_BUTTONS 31 /* chosen because sc_buttons is int */
+
+ u_char qbuf[QUEUE_BUFSIZE]; /* must be divisable by 3&4 */
+ u_char dummy[100]; /* XXX just for safety and for now */
+ int qcount, qhead, qtail;
+ mousehw_t hw;
+ mousemode_t mode;
+ mousestatus_t status;
+
+ int state;
+# define UMS_ASLEEP 0x01 /* readFromDevice is waiting */
+# define UMS_SELECT 0x02 /* select is waiting */
+ struct selinfo rsel; /* process waiting in select */
+
+ struct cdev *dev; /* specfs */
+};
+
+#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
+#define MOUSE_FLAGS (HIO_RELATIVE)
+
+static void ums_intr(usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status status);
+
+static void ums_add_to_queue(struct ums_softc *sc,
+ int dx, int dy, int dz, int dt, int buttons);
+static void ums_add_to_queue_timeout(void *priv);
+
+static int ums_enable(void *);
+static void ums_disable(void *);
+
+static d_open_t ums_open;
+static d_close_t ums_close;
+static d_read_t ums_read;
+static d_ioctl_t ums_ioctl;
+static d_poll_t ums_poll;
+
+
+static struct cdevsw ums_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = ums_open,
+ .d_close = ums_close,
+ .d_read = ums_read,
+ .d_ioctl = ums_ioctl,
+ .d_poll = ums_poll,
+ .d_name = "ums",
+};
+
+static device_probe_t ums_match;
+static device_attach_t ums_attach;
+static device_detach_t ums_detach;
+
+static device_method_t ums_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ums_match),
+ DEVMETHOD(device_attach, ums_attach),
+ DEVMETHOD(device_detach, ums_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ums_driver = {
+ "ums",
+ ums_methods,
+ sizeof(struct ums_softc)
+};
+
+static devclass_t ums_devclass;
+
+static int
+ums_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+ int size, ret;
+ void *desc;
+ usbd_status err;
+
+ if (!uaa->iface)
+ return (UMATCH_NONE);
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (!id || id->bInterfaceClass != UICLASS_HID)
+ return (UMATCH_NONE);
+
+ err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
+ if (err)
+ return (UMATCH_NONE);
+
+ if (hid_is_collection(desc, size,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
+ ret = UMATCH_IFACECLASS;
+ else if (id->bInterfaceClass == UICLASS_HID &&
+ id->bInterfaceSubClass == UISUBCLASS_BOOT &&
+ id->bInterfaceProtocol == UIPROTO_MOUSE)
+ ret = UMATCH_IFACECLASS;
+ else
+ ret = UMATCH_NONE;
+
+ free(desc, M_TEMP);
+ return (ret);
+}
+
+static int
+ums_attach(device_t self)
+{
+ struct ums_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_interface_handle iface = uaa->iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int size;
+ void *desc;
+ usbd_status err;
+ u_int32_t flags;
+ int i, wheel;
+ struct hid_location loc_btn;
+
+ sc->sc_disconnected = 1;
+ sc->sc_iface = iface;
+ id = usbd_get_interface_descriptor(iface);
+ sc->sc_dev = self;
+ ed = usbd_interface2endpoint_descriptor(iface, 0);
+ if (!ed) {
+ printf("%s: could not read endpoint descriptor\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+
+ DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
+ "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
+ " bInterval=%d\n",
+ ed->bLength, ed->bDescriptorType,
+ UE_GET_ADDR(ed->bEndpointAddress),
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
+ UE_GET_XFERTYPE(ed->bmAttributes),
+ UGETW(ed->wMaxPacketSize), ed->bInterval));
+
+ if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
+ UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
+ printf("%s: unexpected endpoint\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+
+ err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
+ if (err)
+ return ENXIO;
+
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
+ hid_input, &sc->sc_loc_x, &flags)) {
+ printf("%s: mouse has no X report\n", device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ printf("%s: X report 0x%04x not supported\n",
+ device_get_nameunit(sc->sc_dev), flags);
+ return ENXIO;
+ }
+
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
+ hid_input, &sc->sc_loc_y, &flags)) {
+ printf("%s: mouse has no Y report\n", device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ printf("%s: Y report 0x%04x not supported\n",
+ device_get_nameunit(sc->sc_dev), flags);
+ return ENXIO;
+ }
+
+ /* Try the wheel first as the Z activator since it's tradition. */
+ wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_WHEEL),
+ hid_input, &sc->sc_loc_z, &flags) ||
+ hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_TWHEEL),
+ hid_input, &sc->sc_loc_z, &flags);
+
+ if (wheel) {
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ printf("\n%s: Wheel report 0x%04x not supported\n",
+ device_get_nameunit(sc->sc_dev), flags);
+ sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
+ } else {
+ sc->flags |= UMS_Z;
+ if (usbd_get_quirks(uaa->device)->uq_flags &
+ UQ_MS_REVZ) {
+ /* Some wheels need the Z axis reversed. */
+ sc->flags |= UMS_REVZ;
+ }
+
+ }
+ /*
+ * We might have both a wheel and Z direction, if so put
+ * put the Z on the W coordinate.
+ */
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_Z),
+ hid_input, &sc->sc_loc_w, &flags)) {
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ printf("\n%s: Z report 0x%04x not supported\n",
+ device_get_nameunit(sc->sc_dev), flags);
+ sc->sc_loc_w.size = 0; /* Bad Z, ignore */
+ }
+ }
+ } else if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_Z),
+ hid_input, &sc->sc_loc_z, &flags)) {
+ if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
+ printf("\n%s: Z report 0x%04x not supported\n",
+ device_get_nameunit(sc->sc_dev), flags);
+ sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
+ } else {
+ sc->flags |= UMS_Z;
+ }
+ }
+
+ /*
+ * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
+ * using 0x0048 (i've called it HUG_TWHEEL) and seems to expect
+ * you to know that the byte after the wheel is the tilt axis.
+ * There are no other HID axis descriptors other than X,Y and
+ * TWHEEL
+ */
+ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
+ hid_input, &sc->sc_loc_t, &flags)) {
+ sc->sc_loc_t.pos = sc->sc_loc_t.pos + 8;
+ sc->flags |= UMS_T;
+ }
+
+ /* figure out the number of buttons */
+ for (i = 1; i <= MAX_BUTTONS; i++)
+ if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
+ hid_input, &loc_btn, 0))
+ break;
+ sc->nbuttons = i - 1;
+ sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons,
+ M_USBDEV, M_NOWAIT);
+ if (!sc->sc_loc_btn) {
+ printf("%s: no memory\n", device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+
+ printf("%s: %d buttons%s%s.\n", device_get_nameunit(sc->sc_dev),
+ sc->nbuttons, sc->flags & UMS_Z? " and Z dir" : "",
+ sc->flags & UMS_T?" and a TILT dir": "");
+
+ for (i = 1; i <= sc->nbuttons; i++)
+ hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
+ hid_input, &sc->sc_loc_btn[i-1], 0);
+
+ sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
+
+ /*
+ * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
+ * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
+ * all of its other button positions are all off. It also reports that
+ * it has two addional buttons and a tilt wheel.
+ */
+ if (usbd_get_quirks(uaa->device)->uq_flags & UQ_MS_BAD_CLASS) {
+ sc->flags = UMS_Z;
+ sc->flags |= UMS_SPUR_BUT_UP;
+ sc->nbuttons = 3;
+ sc->sc_isize = 5;
+ sc->sc_iid = 0;
+ /* 1st byte of descriptor report contains garbage */
+ sc->sc_loc_x.pos = 16;
+ sc->sc_loc_y.pos = 24;
+ sc->sc_loc_z.pos = 32;
+ sc->sc_loc_btn[0].pos = 8;
+ sc->sc_loc_btn[1].pos = 9;
+ sc->sc_loc_btn[2].pos = 10;
+ }
+
+ /*
+ * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has
+ * five Report IDs: 19 23 24 17 18 (in the order they appear in report
+ * descriptor), it seems that report id 17 contains the necessary
+ * mouse information(3-buttons,X,Y,wheel) so we specify it manually.
+ */
+ if (uaa->vendor == USB_VENDOR_MICROSOFT &&
+ uaa->product == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3) {
+ sc->flags = UMS_Z;
+ sc->nbuttons = 3;
+ sc->sc_isize = 5;
+ sc->sc_iid = 17;
+ sc->sc_loc_x.pos = 8;
+ sc->sc_loc_y.pos = 16;
+ sc->sc_loc_z.pos = 24;
+ sc->sc_loc_btn[0].pos = 0;
+ sc->sc_loc_btn[1].pos = 1;
+ sc->sc_loc_btn[2].pos = 2;
+ }
+
+ sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT);
+ if (!sc->sc_ibuf) {
+ printf("%s: no memory\n", device_get_nameunit(sc->sc_dev));
+ free(sc->sc_loc_btn, M_USB);
+ return ENXIO;
+ }
+
+ sc->sc_ep_addr = ed->bEndpointAddress;
+ sc->sc_disconnected = 0;
+ free(desc, M_TEMP);
+
+#ifdef USB_DEBUG
+ DPRINTF(("ums_attach: sc=%p\n", sc));
+ DPRINTF(("ums_attach: X\t%d/%d\n",
+ sc->sc_loc_x.pos, sc->sc_loc_x.size));
+ DPRINTF(("ums_attach: Y\t%d/%d\n",
+ sc->sc_loc_y.pos, sc->sc_loc_y.size));
+ if (sc->flags & UMS_Z)
+ DPRINTF(("ums_attach: Z\t%d/%d\n",
+ sc->sc_loc_z.pos, sc->sc_loc_z.size));
+ for (i = 1; i <= sc->nbuttons; i++) {
+ DPRINTF(("ums_attach: B%d\t%d/%d\n",
+ i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
+ }
+ DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
+#endif
+
+ if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
+ sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->hw.iftype = MOUSE_IF_USB;
+ sc->hw.type = MOUSE_MOUSE;
+ sc->hw.model = MOUSE_MODEL_GENERIC;
+ sc->hw.hwid = 0;
+ sc->mode.protocol = MOUSE_PROTO_MSC;
+ sc->mode.rate = -1;
+ sc->mode.resolution = MOUSE_RES_UNKNOWN;
+ sc->mode.accelfactor = 0;
+ sc->mode.level = 0;
+ sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
+
+ sc->status.flags = 0;
+ sc->status.button = sc->status.obutton = 0;
+ sc->status.dx = sc->status.dy = sc->status.dz = 0;
+
+ sc->dev = make_dev(&ums_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "ums%d", device_get_unit(self));
+
+ callout_init(&sc->callout_handle, 0);
+ if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
+ DPRINTF(("%s: Spurious button up events\n",
+ device_get_nameunit(sc->sc_dev)));
+ sc->flags |= UMS_SPUR_BUT_UP;
+ }
+
+ return 0;
+}
+
+
+static int
+ums_detach(device_t self)
+{
+ struct ums_softc *sc = device_get_softc(self);
+
+ if (sc->sc_enabled)
+ ums_disable(sc);
+
+ DPRINTF(("%s: disconnected\n", device_get_nameunit(self)));
+
+ free(sc->sc_loc_btn, M_USB);
+ free(sc->sc_ibuf, M_USB);
+
+ /* someone waiting for data */
+ /*
+ * XXX If we wakeup the process here, the device will be gone by
+ * the time the process gets a chance to notice. *_close and friends
+ * should be fixed to handle this case.
+ * Or we should do a delayed detach for this.
+ * Does this delay now force tsleep to exit with an error?
+ */
+ if (sc->state & UMS_ASLEEP) {
+ sc->state &= ~UMS_ASLEEP;
+ wakeup(sc);
+ }
+ if (sc->state & UMS_SELECT) {
+ sc->state &= ~UMS_SELECT;
+ selwakeuppri(&sc->rsel, PZERO);
+ }
+
+ destroy_dev(sc->dev);
+
+ return 0;
+}
+
+void
+ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
+{
+ struct ums_softc *sc = addr;
+ u_char *ibuf;
+ int dx, dy, dz, dw, dt;
+ int buttons = 0;
+ int i;
+
+#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
+
+ DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
+ DPRINTFN(5, ("ums_intr: data ="));
+ for (i = 0; i < sc->sc_isize; i++)
+ DPRINTFN(5, (" %02x", sc->sc_ibuf[i]));
+ DPRINTFN(5, ("\n"));
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ums_intr: status=%d\n", status));
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
+ if(status != USBD_IOERROR)
+ return;
+ }
+
+ ibuf = sc->sc_ibuf;
+ /*
+ * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte of
+ * data compared to most USB mice. This byte frequently switches
+ * from 0x01 (usual state) to 0x02. I assume it is to allow
+ * extra, non-standard, reporting (say battery-life). However
+ * at the same time it generates a left-click message on the button
+ * byte which causes spurious left-click's where there shouldn't be.
+ * This should sort that.
+ * Currently it's the only user of UMS_T so use it as an identifier.
+ * We probably should switch to some more official quirk.
+ *
+ * UPDATE: This problem affects the M$ Wireless Notebook Optical Mouse,
+ * too. However, the leading byte for this mouse is normally 0x11,
+ * and the phantom mouse click occurs when its 0x14.
+ */
+ if (sc->flags & UMS_T) {
+ if (sc->sc_iid) {
+ if (*ibuf++ == 0x02)
+ return;
+ }
+ } else if (sc->flags & UMS_SPUR_BUT_UP) {
+ DPRINTFN(5, ("ums_intr: #### ibuf[0] =3D %d ####\n", *ibuf));
+ if (*ibuf == 0x14 || *ibuf == 0x15)
+ return;
+ } else {
+ if (sc->sc_iid) {
+ if (*ibuf++ != sc->sc_iid)
+ return;
+ }
+ }
+
+ dx = hid_get_data(ibuf, &sc->sc_loc_x);
+ dy = -hid_get_data(ibuf, &sc->sc_loc_y);
+ dz = -hid_get_data(ibuf, &sc->sc_loc_z);
+ dw = hid_get_data(ibuf, &sc->sc_loc_w);
+ if (sc->flags & UMS_REVZ)
+ dz = -dz;
+ if (sc->flags & UMS_T)
+ dt = -hid_get_data(ibuf, &sc->sc_loc_t);
+ else
+ dt = 0;
+ for (i = 0; i < sc->nbuttons; i++)
+ if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
+ buttons |= (1 << UMS_BUT(i));
+
+ if (dx || dy || dz || dt || dw || (sc->flags & UMS_Z)
+ || buttons != sc->status.button) {
+ DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d w:%d t:%d buttons:0x%x\n",
+ dx, dy, dz, dw, dt, buttons));
+
+ sc->status.button = buttons;
+ sc->status.dx += dx;
+ sc->status.dy += dy;
+ sc->status.dz += dz;
+ /* sc->status.dt += dt; */ /* no way to export this yet */
+ /* sc->status.dw += dw; */ /* idem */
+
+ /* Discard data in case of full buffer */
+ if (sc->qcount == sizeof(sc->qbuf)) {
+ DPRINTF(("Buffer full, discarded packet"));
+ return;
+ }
+
+ /*
+ * The Qtronix keyboard has a built in PS/2 port for a mouse.
+ * The firmware once in a while posts a spurious button up
+ * event. This event we ignore by doing a timeout for 50 msecs.
+ * If we receive dx=dy=dz=buttons=0 before we add the event to
+ * the queue.
+ * In any other case we delete the timeout event.
+ */
+ if (sc->flags & UMS_SPUR_BUT_UP &&
+ dx == 0 && dy == 0 && dz == 0 && dt == 0 && buttons == 0) {
+ callout_reset(&sc->callout_handle, MS_TO_TICKS(50),
+ ums_add_to_queue_timeout, (void *) sc);
+ } else {
+ callout_stop(&sc->callout_handle);
+ ums_add_to_queue(sc, dx, dy, dz, dt, buttons);
+ }
+ }
+}
+
+static void
+ums_add_to_queue_timeout(void *priv)
+{
+ struct ums_softc *sc = priv;
+ int s;
+
+ s = splusb();
+ ums_add_to_queue(sc, 0, 0, 0, 0, 0);
+ splx(s);
+}
+
+static void
+ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int dt, int buttons)
+{
+ /* Discard data in case of full buffer */
+ if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
+ DPRINTF(("Buffer full, discarded packet"));
+ return;
+ }
+
+ if (dx > 254) dx = 254;
+ if (dx < -256) dx = -256;
+ if (dy > 254) dy = 254;
+ if (dy < -256) dy = -256;
+ if (dz > 126) dz = 126;
+ if (dz < -128) dz = -128;
+ if (dt > 126) dt = 126;
+ if (dt < -128) dt = -128;
+
+ sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
+ sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
+ sc->qbuf[sc->qhead+1] = dx >> 1;
+ sc->qbuf[sc->qhead+2] = dy >> 1;
+ sc->qbuf[sc->qhead+3] = dx - (dx >> 1);
+ sc->qbuf[sc->qhead+4] = dy - (dy >> 1);
+
+ if (sc->mode.level == 1) {
+ sc->qbuf[sc->qhead+5] = dz >> 1;
+ sc->qbuf[sc->qhead+6] = dz - (dz >> 1);
+ sc->qbuf[sc->qhead+7] = ((~buttons >> 3)
+ & MOUSE_SYS_EXTBUTTONS);
+ }
+
+ sc->qhead += sc->mode.packetsize;
+ sc->qcount += sc->mode.packetsize;
+ /* wrap round at end of buffer */
+ if (sc->qhead >= sizeof(sc->qbuf))
+ sc->qhead = 0;
+
+ /* someone waiting for data */
+ if (sc->state & UMS_ASLEEP) {
+ sc->state &= ~UMS_ASLEEP;
+ wakeup(sc);
+ }
+ if (sc->state & UMS_SELECT) {
+ sc->state &= ~UMS_SELECT;
+ selwakeuppri(&sc->rsel, PZERO);
+ }
+}
+static int
+ums_enable(v)
+ void *v;
+{
+ struct ums_softc *sc = v;
+
+ usbd_status err;
+
+ if (sc->sc_enabled)
+ return EBUSY;
+
+ sc->sc_enabled = 1;
+ sc->qcount = 0;
+ sc->qhead = sc->qtail = 0;
+ sc->status.flags = 0;
+ sc->status.button = sc->status.obutton = 0;
+ sc->status.dx = sc->status.dy = sc->status.dz /* = sc->status.dt */ = 0;
+
+ callout_handle_init((struct callout_handle *)&sc->callout_handle);
+
+ /*
+ * Force the report (non-boot) protocol.
+ *
+ * Mice without boot protocol support may choose not to implement
+ * Set_Protocol at all; do not check for error.
+ */
+ usbd_set_protocol(sc->sc_iface, 1);
+
+ /* Set up interrupt pipe. */
+ err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
+ USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
+ sc->sc_ibuf, sc->sc_isize, ums_intr,
+ USBD_DEFAULT_INTERVAL);
+ if (err) {
+ DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
+ err));
+ sc->sc_enabled = 0;
+ return (EIO);
+ }
+ return (0);
+}
+
+static void
+ums_disable(priv)
+ void *priv;
+{
+ struct ums_softc *sc = priv;
+
+ callout_stop(&sc->callout_handle);
+
+ /* Disable interrupts. */
+ usbd_abort_pipe(sc->sc_intrpipe);
+ usbd_close_pipe(sc->sc_intrpipe);
+
+ sc->sc_enabled = 0;
+
+ if (sc->qcount != 0)
+ DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
+}
+
+static int
+ums_open(struct cdev *dev, int flag, int fmt, struct thread *p)
+{
+ struct ums_softc *sc;
+
+ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ if (sc == NULL)
+ return (ENXIO);
+
+ return ums_enable(sc);
+}
+
+static int
+ums_close(struct cdev *dev, int flag, int fmt, struct thread *p)
+{
+ struct ums_softc *sc;
+
+ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ if (!sc)
+ return 0;
+
+ if (sc->sc_enabled)
+ ums_disable(sc);
+
+ return 0;
+}
+
+static int
+ums_read(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct ums_softc *sc;
+ int s;
+ char buf[sizeof(sc->qbuf)];
+ int l = 0;
+ int error;
+
+ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ s = splusb();
+ if (!sc) {
+ splx(s);
+ return EIO;
+ }
+
+ while (sc->qcount == 0 ) {
+ if (flag & O_NONBLOCK) { /* non-blocking I/O */
+ splx(s);
+ return EWOULDBLOCK;
+ }
+
+ sc->state |= UMS_ASLEEP; /* blocking I/O */
+ error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
+ if (error) {
+ splx(s);
+ return error;
+ } else if (!sc->sc_enabled) {
+ splx(s);
+ return EINTR;
+ }
+ /* check whether the device is still there */
+
+ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ if (!sc) {
+ splx(s);
+ return EIO;
+ }
+ }
+
+ /*
+ * XXX we could optimise the use of splx/splusb somewhat. The writer
+ * process only extends qcount and qtail. We could copy them and use the copies
+ * to do the copying out of the queue.
+ */
+
+ while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
+ l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
+ if (l > sizeof(buf))
+ l = sizeof(buf);
+ if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */
+ l = sizeof(sc->qbuf) - sc->qtail;
+
+ splx(s);
+ uiomove(&sc->qbuf[sc->qtail], l, uio);
+ s = splusb();
+
+ if ( sc->qcount - l < 0 ) {
+ DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
+ sc->qcount = l;
+ }
+ sc->qcount -= l; /* remove the bytes from the buffer */
+ sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
+ }
+ splx(s);
+
+ return 0;
+}
+
+static int
+ums_poll(struct cdev *dev, int events, struct thread *p)
+{
+ struct ums_softc *sc;
+ int revents = 0;
+ int s;
+
+ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ if (!sc)
+ return 0;
+
+ s = splusb();
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sc->qcount) {
+ revents = events & (POLLIN | POLLRDNORM);
+ } else {
+ sc->state |= UMS_SELECT;
+ selrecord(p, &sc->rsel);
+ }
+ }
+ splx(s);
+
+ return revents;
+}
+
+int
+ums_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *p)
+{
+ struct ums_softc *sc;
+ int error = 0;
+ int s;
+ mousemode_t mode;
+
+ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
+ if (!sc)
+ return EIO;
+
+ switch(cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->hw;
+ break;
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->mode;
+ break;
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+
+ if (mode.level == -1)
+ /* don't change the current setting */
+ ;
+ else if ((mode.level < 0) || (mode.level > 1))
+ return (EINVAL);
+
+ s = splusb();
+ sc->mode.level = mode.level;
+
+ if (sc->mode.level == 0) {
+ if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
+ sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_MSC;
+ sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->mode.level == 1) {
+ if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
+ sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+
+ bzero(sc->qbuf, sizeof(sc->qbuf));
+ sc->qhead = sc->qtail = sc->qcount = 0;
+ splx(s);
+
+ break;
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->mode.level;
+ break;
+ case MOUSE_SETLEVEL:
+ if (*(int *)addr < 0 || *(int *)addr > 1)
+ return (EINVAL);
+
+ s = splusb();
+ sc->mode.level = *(int *)addr;
+
+ if (sc->mode.level == 0) {
+ if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
+ sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_MSC;
+ sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->mode.level == 1) {
+ if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
+ sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->hw.buttons = sc->nbuttons;
+ sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+
+ bzero(sc->qbuf, sizeof(sc->qbuf));
+ sc->qhead = sc->qtail = sc->qcount = 0;
+ splx(s);
+
+ break;
+ case MOUSE_GETSTATUS: {
+ mousestatus_t *status = (mousestatus_t *) addr;
+
+ s = splusb();
+ *status = sc->status;
+ sc->status.obutton = sc->status.button;
+ sc->status.button = 0;
+ sc->status.dx = sc->status.dy
+ = sc->status.dz = /* sc->status.dt = */ 0;
+ splx(s);
+
+ if (status->dx || status->dy || status->dz /* || status->dt */)
+ status->flags |= MOUSE_POSCHANGED;
+ if (status->button != status->obutton)
+ status->flags |= MOUSE_BUTTONSCHANGED;
+ break;
+ }
+ default:
+ error = ENOTTY;
+ }
+
+ return error;
+}
+
+MODULE_DEPEND(ums, usb, 1, 1, 1);
+DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/uplcom.c b/sys/legacy/dev/usb/uplcom.c
new file mode 100644
index 0000000..ab5ab93
--- /dev/null
+++ b/sys/legacy/dev/usb/uplcom.c
@@ -0,0 +1,990 @@
+/* $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * 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.
+ */
+
+/*
+ * This driver supports several USB-to-RS232 serial adapters driven by
+ * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
+ * bridge chip. The adapters are sold under many different brand
+ * names.
+ *
+ * Datasheets are available at Prolific www site at
+ * http://www.prolific.com.tw. The datasheets don't contain full
+ * programming information for the chip.
+ *
+ * PL-2303HX is probably programmed the same as PL-2303X.
+ *
+ * There are several differences between PL-2303 and PL-2303(H)X.
+ * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
+ * different command for controlling CRTSCTS and needs special
+ * sequence of commands for initialization which aren't also
+ * documented in the datasheet.
+ */
+
+#include "opt_uplcom.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/serial.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
+#ifdef USB_DEBUG
+static int uplcomdebug = 0;
+SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW,
+ &uplcomdebug, 0, "uplcom debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (uplcomdebug > (n)) \
+ printf x; \
+ } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UPLCOM_MODVER 1 /* module version */
+
+#define UPLCOM_CONFIG_INDEX 0
+#define UPLCOM_IFACE_INDEX 0
+#define UPLCOM_SECOND_IFACE_INDEX 1
+
+#ifndef UPLCOM_INTR_INTERVAL
+#define UPLCOM_INTR_INTERVAL 100 /* ms */
+#endif
+
+#define UPLCOM_SET_REQUEST 0x01
+#define UPLCOM_SET_CRTSCTS 0x41
+#define UPLCOM_SET_CRTSCTS_PL2303X 0x61
+#define RSAQ_STATUS_CTS 0x80
+#define RSAQ_STATUS_DSR 0x02
+#define RSAQ_STATUS_DCD 0x01
+
+#define TYPE_PL2303 0
+#define TYPE_PL2303X 1
+
+struct uplcom_softc {
+ struct ucom_softc sc_ucom;
+
+ int sc_iface_number; /* interface number */
+
+ usbd_interface_handle sc_intr_iface; /* interrupt interface */
+ int sc_intr_number; /* interrupt number */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
+ u_char *sc_intr_buf; /* interrupt buffer */
+ int sc_isize;
+
+ usb_cdc_line_state_t sc_line_state; /* current line state */
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+ u_char sc_status;
+
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* uplcom status register */
+
+ int sc_chiptype; /* Type of chip */
+
+ struct task sc_task;
+};
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * The output buffer size cannot be increased due to the size encoding.
+ */
+#define UPLCOMIBUFSIZE 256
+#define UPLCOMOBUFSIZE 256
+
+static usbd_status uplcom_reset(struct uplcom_softc *);
+static usbd_status uplcom_set_line_coding(struct uplcom_softc *,
+ usb_cdc_line_state_t *);
+static usbd_status uplcom_set_crtscts(struct uplcom_softc *);
+static void uplcom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+
+static void uplcom_set(void *, int, int, int);
+static void uplcom_dtr(struct uplcom_softc *, int);
+static void uplcom_rts(struct uplcom_softc *, int);
+static void uplcom_break(struct uplcom_softc *, int);
+static void uplcom_set_line_state(struct uplcom_softc *);
+static void uplcom_get_status(void *, int, u_char *, u_char *);
+static int uplcom_param(void *, int, struct termios *);
+static int uplcom_open(void *, int);
+static void uplcom_close(void *, int);
+static void uplcom_notify(void *, int);
+
+struct ucom_callback uplcom_callback = {
+ uplcom_get_status,
+ uplcom_set,
+ uplcom_param,
+ NULL,
+ uplcom_open,
+ uplcom_close,
+ NULL,
+ NULL
+};
+
+static const struct uplcom_product {
+ uint16_t vendor;
+ uint16_t product;
+ int32_t release; /* release is a 16bit entity,
+ * if -1 is specified we "don't care"
+ * This is a floor value. The table
+ * must have newer revs before older
+ * revs (and -1 last).
+ */
+ char chiptype;
+} uplcom_products [] = {
+ { USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, -1, TYPE_PL2303 },
+
+ /* I/O DATA USB-RSAQ */
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, -1, TYPE_PL2303 },
+ /* Prolific Pharos */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, -1, TYPE_PL2303 },
+ /* I/O DATA USB-RSAQ2 */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, -1, TYPE_PL2303 },
+ /* I/O DATA USB-RSAQ3 */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, -1, TYPE_PL2303X },
+ /* Willcom W-SIM*/
+ { USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, -1, TYPE_PL2303X},
+ /* PLANEX USB-RS232 URS-03 */
+ { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, -1, TYPE_PL2303 },
+ /* TrendNet TU-S9 */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303,
+ 0x400, TYPE_PL2303X },
+ /* ST Lab USB-SERIAL-4 */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303,
+ 0x300, TYPE_PL2303X },
+ /* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, -1, TYPE_PL2303 },
+ /* TDK USB-PHS Adapter UHA6400 */
+ { USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, -1, TYPE_PL2303 },
+ /* RATOC REX-USB60 */
+ { USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, -1, TYPE_PL2303 },
+ /* ELECOM UC-SGT */
+ { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, -1, TYPE_PL2303 },
+ { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, -1, TYPE_PL2303 },
+ /* Sagem USB-Serial Controller */
+ { USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, -1, TYPE_PL2303X },
+ /* Sony Ericsson USB Cable */
+ { USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10,
+ -1,TYPE_PL2303 },
+ /* SOURCENEXT KeikaiDenwa 8 */
+ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8,
+ -1, TYPE_PL2303 },
+ /* SOURCENEXT KeikaiDenwa 8 with charger */
+ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG,
+ -1, TYPE_PL2303 },
+ /* HAL Corporation Crossam2+USB */
+ { USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, -1, TYPE_PL2303 },
+ /* Sitecom USB to Serial */
+ { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, -1, TYPE_PL2303 },
+ /* Tripp-Lite U209-000-R */
+ { USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, -1, TYPE_PL2303X },
+ /* Belkin F5U257 */
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, -1, TYPE_PL2303X },
+ { 0, 0 }
+};
+
+static device_probe_t uplcom_match;
+static device_attach_t uplcom_attach;
+static device_detach_t uplcom_detach;
+
+static device_method_t uplcom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uplcom_match),
+ DEVMETHOD(device_attach, uplcom_attach),
+ DEVMETHOD(device_detach, uplcom_detach),
+ { 0, 0 }
+};
+
+static driver_t uplcom_driver = {
+ "ucom",
+ uplcom_methods,
+ sizeof (struct uplcom_softc)
+};
+
+DRIVER_MODULE(uplcom, uhub, uplcom_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uplcom, usb, 1, 1, 1);
+MODULE_DEPEND(uplcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(uplcom, UPLCOM_MODVER);
+
+static int uplcominterval = UPLCOM_INTR_INTERVAL;
+
+static int
+sysctl_hw_usb_uplcom_interval(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = uplcominterval;
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+ if (0 < val && val <= 1000)
+ uplcominterval = val;
+ else
+ err = EINVAL;
+
+ return (err);
+}
+
+SYSCTL_PROC(_hw_usb_uplcom, OID_AUTO, interval, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_usb_uplcom_interval,
+ "I", "uplcom interrupt pipe interval");
+
+static int
+uplcom_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ int i;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ for (i = 0; uplcom_products[i].vendor != 0; i++) {
+ if (uplcom_products[i].vendor == uaa->vendor &&
+ uplcom_products[i].product == uaa->product &&
+ (uplcom_products[i].release <= uaa->release ||
+ uplcom_products[i].release == -1)) {
+ return (UMATCH_VENDOR_PRODUCT);
+ }
+ }
+ return (UMATCH_NONE);
+}
+
+static int
+uplcom_attach(device_t self)
+{
+ struct uplcom_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ const char *devname;
+ usbd_status err;
+ int i;
+
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ devname = device_get_nameunit(ucom->sc_dev);
+
+ DPRINTF(("uplcom attach: sc = %p\n", sc));
+
+ /* determine chip type */
+ for (i = 0; uplcom_products[i].vendor != 0; i++) {
+ if (uplcom_products[i].vendor == uaa->vendor &&
+ uplcom_products[i].product == uaa->product &&
+ (uplcom_products[i].release == uaa->release ||
+ uplcom_products[i].release == -1)) {
+ sc->sc_chiptype = uplcom_products[i].chiptype;
+ break;
+ }
+ }
+
+ /*
+ * check we found the device - attach should have ensured we
+ * don't get here without matching device
+ */
+ if (uplcom_products[i].vendor == 0) {
+ printf("%s: didn't match\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+#ifdef USB_DEBUG
+ /* print the chip type */
+ if (sc->sc_chiptype == TYPE_PL2303X) {
+ DPRINTF(("uplcom_attach: chiptype 2303X\n"));
+ } else {
+ DPRINTF(("uplcom_attach: chiptype 2303\n"));
+ }
+#endif
+
+ /* initialize endpoints */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UPLCOM_CONFIG_INDEX, 1);
+ if (err) {
+ printf("%s: failed to set configuration: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the config descriptor */
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n",
+ device_get_nameunit(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* get the (first/common) interface */
+ err = usbd_device2interface_handle(dev, UPLCOM_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get interface: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* Find the interrupt endpoints */
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ device_get_nameunit(ucom->sc_dev), i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ if (sc->sc_intr_number == -1) {
+ printf("%s: Could not find interrupt in\n",
+ device_get_nameunit(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ /* keep interface for interrupt */
+ sc->sc_intr_iface = ucom->sc_iface;
+
+ /*
+ * USB-RSAQ1 has two interface
+ *
+ * USB-RSAQ1 | USB-RSAQ2
+ * -----------------+-----------------
+ * Interface 0 |Interface 0
+ * Interrupt(0x81) | Interrupt(0x81)
+ * -----------------+ BulkIN(0x02)
+ * Interface 1 | BulkOUT(0x83)
+ * BulkIN(0x02) |
+ * BulkOUT(0x83) |
+ */
+ if (cdesc->bNumInterface == 2) {
+ err = usbd_device2interface_handle(dev,
+ UPLCOM_SECOND_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ printf("%s: failed to get second interface: %s\n",
+ devname, usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+ }
+
+ /* Find the bulk{in,out} endpoints */
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for %d\n",
+ device_get_nameunit(ucom->sc_dev), i);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+ }
+
+ if (ucom->sc_bulkin_no == -1) {
+ printf("%s: Could not find data bulk in\n",
+ device_get_nameunit(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ if (ucom->sc_bulkout_no == -1) {
+ printf("%s: Could not find data bulk out\n",
+ device_get_nameunit(ucom->sc_dev));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ sc->sc_dtr = sc->sc_rts = -1;
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UPLCOMIBUFSIZE;
+ ucom->sc_obufsize = UPLCOMOBUFSIZE;
+ ucom->sc_ibufsizepad = UPLCOMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uplcom_callback;
+
+ err = uplcom_reset(sc);
+
+ if (err) {
+ printf("%s: reset failed: %s\n",
+ device_get_nameunit(ucom->sc_dev), usbd_errstr(err));
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+ DPRINTF(("uplcom: in = 0x%x, out = 0x%x, intr = 0x%x\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no, sc->sc_intr_number));
+
+ TASK_INIT(&sc->sc_task, 0, uplcom_notify, sc);
+ ucom_attach(&sc->sc_ucom);
+ return 0;
+
+error:
+ return ENXIO;
+}
+
+static int
+uplcom_detach(device_t self)
+{
+ struct uplcom_softc *sc = device_get_softc(self);
+ int rv = 0;
+
+ DPRINTF(("uplcom_detach: sc = %p\n", sc));
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ sc->sc_ucom.sc_dying = 1;
+
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
+
+static usbd_status
+uplcom_reset(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UPLCOM_SET_REQUEST;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err) {
+ printf("%s: uplcom_reset: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (EIO);
+ }
+
+ return (0);
+}
+
+struct pl2303x_init {
+ uint8_t req_type;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+};
+
+static const struct pl2303x_init pl2303x[] = {
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0 }
+};
+#define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0]))
+
+static usbd_status
+uplcom_pl2303x_init(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ int i;
+
+ for (i = 0; i < N_PL2302X_INIT; i++) {
+ req.bmRequestType = pl2303x[i].req_type;
+ req.bRequest = pl2303x[i].request;
+ USETW(req.wValue, pl2303x[i].value);
+ USETW(req.wIndex, pl2303x[i].index);
+ USETW(req.wLength, pl2303x[i].length);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err) {
+ printf("%s: uplcom_pl2303x_init: %d: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), i,
+ usbd_errstr(err));
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+static void
+uplcom_set_line_state(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ int ls;
+ usbd_status err;
+
+ ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
+ (sc->sc_rts ? UCDC_LINE_RTS : 0);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, ls);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err)
+ printf("%s: uplcom_set_line_status: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+}
+
+static void
+uplcom_set(void *addr, int portno, int reg, int onoff)
+{
+ struct uplcom_softc *sc = addr;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ uplcom_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ uplcom_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ uplcom_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+uplcom_dtr(struct uplcom_softc *sc, int onoff)
+{
+ DPRINTF(("uplcom_dtr: onoff = %d\n", onoff));
+
+ if (sc->sc_dtr == onoff)
+ return;
+ sc->sc_dtr = onoff;
+
+ uplcom_set_line_state(sc);
+}
+
+static void
+uplcom_rts(struct uplcom_softc *sc, int onoff)
+{
+ DPRINTF(("uplcom_rts: onoff = %d\n", onoff));
+
+ if (sc->sc_rts == onoff)
+ return;
+ sc->sc_rts = onoff;
+
+ uplcom_set_line_state(sc);
+}
+
+static void
+uplcom_break(struct uplcom_softc *sc, int onoff)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("uplcom_break: onoff = %d\n", onoff));
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err)
+ printf("%s: uplcom_break: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+}
+
+static usbd_status
+uplcom_set_crtscts(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("uplcom_set_crtscts: on\n"));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UPLCOM_SET_REQUEST;
+ USETW(req.wValue, 0);
+ if (sc->sc_chiptype == TYPE_PL2303X)
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
+ else
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err) {
+ printf("%s: uplcom_set_crtscts: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static usbd_status
+uplcom_set_line_coding(struct uplcom_softc *sc, usb_cdc_line_state_t *state)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF((
+"uplcom_set_line_coding: rate = %d, fmt = %d, parity = %d bits = %d\n",
+ UGETDW(state->dwDTERate), state->bCharFormat,
+ state->bParityType, state->bDataBits));
+
+ if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) {
+ DPRINTF(("uplcom_set_line_coding: already set\n"));
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_iface_number);
+ USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, state);
+ if (err) {
+ printf("%s: uplcom_set_line_coding: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), usbd_errstr(err));
+ return (err);
+ }
+
+ sc->sc_line_state = *state;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static const int uplcom_rates[] = {
+ 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
+ 19200, 28800, 38400, 57600, 115200,
+ /*
+ * Higher speeds are probably possible. PL2303X supports up to
+ * 6Mb and can set any rate
+ */
+ 230400, 460800, 614400, 921600, 1228800
+};
+#define N_UPLCOM_RATES (sizeof(uplcom_rates)/sizeof(uplcom_rates[0]))
+
+static int
+uplcom_param(void *addr, int portno, struct termios *t)
+{
+ struct uplcom_softc *sc = addr;
+ usbd_status err;
+ usb_cdc_line_state_t ls;
+ int i;
+
+ DPRINTF(("uplcom_param: sc = %p\n", sc));
+
+ /* Check requested baud rate */
+ for (i = 0; i < N_UPLCOM_RATES; i++)
+ if (uplcom_rates[i] == t->c_ospeed)
+ break;
+ if (i == N_UPLCOM_RATES) {
+ DPRINTF(("uplcom_param: bad baud rate (%d)\n", t->c_ospeed));
+ return (EIO);
+ }
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+ if (ISSET(t->c_cflag, CSTOPB))
+ ls.bCharFormat = UCDC_STOP_BIT_2;
+ else
+ ls.bCharFormat = UCDC_STOP_BIT_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ ls.bParityType = UCDC_PARITY_ODD;
+ else
+ ls.bParityType = UCDC_PARITY_EVEN;
+ } else
+ ls.bParityType = UCDC_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ err = uplcom_set_line_coding(sc, &ls);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CRTSCTS)) {
+ err = uplcom_set_crtscts(sc);
+ if (err)
+ return (EIO);
+ }
+
+ return (0);
+}
+
+static int
+uplcom_open(void *addr, int portno)
+{
+ struct uplcom_softc *sc = addr;
+ int err;
+
+ if (sc->sc_ucom.sc_dying)
+ return (ENXIO);
+
+ DPRINTF(("uplcom_open: sc = %p\n", sc));
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ sc->sc_status = 0; /* clear status bit */
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_intr_iface,
+ sc->sc_intr_number,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intr_pipe,
+ sc,
+ sc->sc_intr_buf,
+ sc->sc_isize,
+ uplcom_intr,
+ uplcominterval);
+ if (err) {
+ printf("%s: cannot open interrupt pipe (addr %d)\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev),
+ sc->sc_intr_number);
+ return (EIO);
+ }
+ }
+
+ if (sc->sc_chiptype == TYPE_PL2303X)
+ return (uplcom_pl2303x_init(sc));
+
+ return (0);
+}
+
+static void
+uplcom_close(void *addr, int portno)
+{
+ struct uplcom_softc *sc = addr;
+ int err;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ DPRINTF(("uplcom_close: close\n"));
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: abort interrupt pipe failed: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ printf("%s: close interrupt pipe failed: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev),
+ usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
+
+static void
+uplcom_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct uplcom_softc *sc = priv;
+ u_char *buf = sc->sc_intr_buf;
+ u_char pstatus;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ DPRINTF(("%s: uplcom_intr: abnormal status: %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev),
+ usbd_errstr(status)));
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ DPRINTF(("%s: uplcom status = %02x\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), buf[8]));
+
+ sc->sc_lsr = sc->sc_msr = 0;
+ pstatus = buf[8];
+ if (ISSET(pstatus, RSAQ_STATUS_CTS))
+ sc->sc_msr |= SER_CTS;
+ else
+ sc->sc_msr &= ~SER_CTS;
+ if (ISSET(pstatus, RSAQ_STATUS_DSR))
+ sc->sc_msr |= SER_DSR;
+ else
+ sc->sc_msr &= ~SER_DSR;
+ if (ISSET(pstatus, RSAQ_STATUS_DCD))
+ sc->sc_msr |= SER_DCD;
+ else
+ sc->sc_msr &= ~SER_DCD;
+
+ /* Deferred notifying to the ucom layer */
+ taskqueue_enqueue(taskqueue_swi_giant, &sc->sc_task);
+}
+
+static void
+uplcom_notify(void *arg, int count)
+{
+ struct uplcom_softc *sc;
+
+ sc = (struct uplcom_softc *)arg;
+ if (sc->sc_ucom.sc_dying)
+ return;
+ ucom_status_change(&sc->sc_ucom);
+}
+
+static void
+uplcom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct uplcom_softc *sc = addr;
+
+ DPRINTF(("uplcom_get_status:\n"));
+
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
diff --git a/sys/legacy/dev/usb/urio.c b/sys/legacy/dev/usb/urio.c
new file mode 100644
index 0000000..5f69918
--- /dev/null
+++ b/sys/legacy/dev/usb/urio.c
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 2000 Iwasa Kazmi
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+/*
+ * 2000/3/24 added NetBSD/OpenBSD support (from Alex Nemirovsky)
+ * 2000/3/07 use two bulk-pipe handles for read and write (Dirk)
+ * 2000/3/06 change major number(143), and copyright header
+ * some fix for 4.0 (Dirk)
+ * 2000/3/05 codes for FreeBSD 4.x - CURRENT (Thanks to Dirk-Willem van Gulik)
+ * 2000/3/01 remove retry code from urioioctl()
+ * change method of bulk transfer (no interrupt)
+ * 2000/2/28 small fixes for new rio_usb.h
+ * 2000/2/24 first version.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include "usbdevs.h"
+#include <dev/usb/rio500_usb.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uriodebug) printf x
+#define DPRINTFN(n,x) if (uriodebug>(n)) printf x
+int uriodebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio");
+SYSCTL_INT(_hw_usb_urio, OID_AUTO, debug, CTLFLAG_RW,
+ &uriodebug, 0, "urio debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+/* difference of usbd interface */
+#define USBDI 1
+
+#define RIO_OUT 0
+#define RIO_IN 1
+#define RIO_NODIR 2
+
+d_open_t urioopen;
+d_close_t urioclose;
+d_read_t urioread;
+d_write_t uriowrite;
+d_ioctl_t urioioctl;
+
+
+static struct cdevsw urio_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = urioopen,
+ .d_close = urioclose,
+ .d_read = urioread,
+ .d_write = uriowrite,
+ .d_ioctl = urioioctl,
+ .d_name = "urio",
+};
+#define RIO_UE_GET_DIR(p) ((UE_GET_DIR(p) == UE_DIR_IN) ? RIO_IN :\
+ ((UE_GET_DIR(p) == UE_DIR_OUT) ? RIO_OUT :\
+ RIO_NODIR))
+
+#define URIO_BBSIZE 1024
+
+struct urio_softc {
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+
+ int sc_opened;
+ usbd_pipe_handle sc_pipeh_in;
+ usbd_pipe_handle sc_pipeh_out;
+ int sc_epaddr[2];
+
+ int sc_refcnt;
+ struct cdev *sc_dev_t;
+ u_char sc_dying;
+};
+
+#define URIOUNIT(n) (dev2unit(n))
+
+#define RIO_RW_TIMEOUT 4000 /* ms */
+
+static device_probe_t urio_match;
+static device_attach_t urio_attach;
+static device_detach_t urio_detach;
+
+static device_method_t urio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, urio_match),
+ DEVMETHOD(device_attach, urio_attach),
+ DEVMETHOD(device_detach, urio_detach),
+
+ { 0, 0 }
+};
+
+static driver_t urio_driver = {
+ "urio",
+ urio_methods,
+ sizeof(struct urio_softc)
+};
+
+static devclass_t urio_devclass;
+
+static int
+urio_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_device_descriptor_t *dd;
+
+ DPRINTFN(10,("urio_match\n"));
+ if (!uaa->iface)
+ return UMATCH_NONE;
+
+ dd = usbd_get_device_descriptor(uaa->device);
+
+ if (dd &&
+ ((UGETW(dd->idVendor) == USB_VENDOR_DIAMOND &&
+ UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND_RIO500USB) ||
+ (UGETW(dd->idVendor) == USB_VENDOR_DIAMOND2 &&
+ (UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO600USB ||
+ UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO800USB))))
+ return UMATCH_VENDOR_PRODUCT;
+ else
+ return UMATCH_NONE;
+}
+
+static int
+urio_attach(device_t self)
+{
+ struct urio_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle udev;
+ usbd_interface_handle iface;
+ u_int8_t epcount;
+ usbd_status r;
+ char * ermsg = "<none>";
+ int i;
+
+ DPRINTFN(10,("urio_attach: sc=%p\n", sc));
+ sc->sc_dev = self;
+ sc->sc_udev = udev = uaa->device;
+
+ if ((!uaa->device) || (!uaa->iface)) {
+ ermsg = "device or iface";
+ goto nobulk;
+ }
+ sc->sc_iface = iface = uaa->iface;
+ sc->sc_opened = 0;
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ sc->sc_refcnt = 0;
+
+ r = usbd_endpoint_count(iface, &epcount);
+ if (r != USBD_NORMAL_COMPLETION) {
+ ermsg = "endpoints";
+ goto nobulk;
+ }
+
+ sc->sc_epaddr[RIO_OUT] = 0xff;
+ sc->sc_epaddr[RIO_IN] = 0x00;
+
+ for (i = 0; i < epcount; i++) {
+ usb_endpoint_descriptor_t *edesc =
+ usbd_interface2endpoint_descriptor(iface, i);
+ int d;
+
+ if (!edesc) {
+ ermsg = "interface endpoint";
+ goto nobulk;
+ }
+
+ d = RIO_UE_GET_DIR(edesc->bEndpointAddress);
+ if (d != RIO_NODIR)
+ sc->sc_epaddr[d] = edesc->bEndpointAddress;
+ }
+ if ( sc->sc_epaddr[RIO_OUT] == 0xff ||
+ sc->sc_epaddr[RIO_IN] == 0x00) {
+ ermsg = "Rio I&O";
+ goto nobulk;
+ }
+
+ sc->sc_dev_t = make_dev(&urio_cdevsw, device_get_unit(self),
+ UID_ROOT, GID_OPERATOR,
+ 0644, "urio%d", device_get_unit(self));
+ DPRINTFN(10, ("urio_attach: %p\n", sc->sc_udev));
+
+ return 0;
+
+ nobulk:
+ printf("%s: could not find %s\n", device_get_nameunit(sc->sc_dev),ermsg);
+ return ENXIO;
+}
+
+
+int
+urioopen(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct urio_softc * sc;
+ int unit = URIOUNIT(dev);
+ sc = devclass_get_softc(urio_devclass, unit);
+ if (sc == NULL)
+ return (ENXIO);
+
+ DPRINTFN(5, ("urioopen: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, unit));
+
+ if (sc->sc_opened)
+ return EBUSY;
+
+ if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))
+ return EACCES;
+
+ sc->sc_opened = 1;
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ if (usbd_open_pipe(sc->sc_iface,
+ sc->sc_epaddr[RIO_IN], 0, &sc->sc_pipeh_in)
+ != USBD_NORMAL_COMPLETION)
+ {
+ sc->sc_pipeh_in = 0;
+ return EIO;
+ };
+ if (usbd_open_pipe(sc->sc_iface,
+ sc->sc_epaddr[RIO_OUT], 0, &sc->sc_pipeh_out)
+ != USBD_NORMAL_COMPLETION)
+ {
+ usbd_close_pipe(sc->sc_pipeh_in);
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ return EIO;
+ };
+ return 0;
+}
+
+int
+urioclose(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct urio_softc * sc;
+ int unit = URIOUNIT(dev);
+ sc = devclass_get_softc(urio_devclass, unit);
+
+ DPRINTFN(5, ("urioclose: flag=%d, mode=%d, unit=%d\n", flag, mode, unit));
+ if (sc->sc_pipeh_in)
+ usbd_close_pipe(sc->sc_pipeh_in);
+
+ if (sc->sc_pipeh_out)
+ usbd_close_pipe(sc->sc_pipeh_out);
+
+ sc->sc_pipeh_in = 0;
+ sc->sc_pipeh_out = 0;
+ sc->sc_opened = 0;
+ sc->sc_refcnt = 0;
+ return 0;
+}
+
+int
+urioread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct urio_softc * sc;
+ usbd_xfer_handle reqh;
+ int unit = URIOUNIT(dev);
+ usbd_status r;
+ char buf[URIO_BBSIZE];
+ u_int32_t n, tn;
+ int error = 0;
+
+ sc = devclass_get_softc(urio_devclass, unit);
+
+ DPRINTFN(5, ("urioread: %d\n", unit));
+ if (!sc->sc_opened)
+ return EIO;
+
+ sc->sc_refcnt++;
+ reqh = usbd_alloc_xfer(sc->sc_udev);
+ if (reqh == 0)
+ return ENOMEM;
+ while ((n = min(URIO_BBSIZE, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("urioread: start transfer %d bytes\n", n));
+ tn = n;
+ usbd_setup_xfer(reqh, sc->sc_pipeh_in, 0, buf, tn,
+ 0, RIO_RW_TIMEOUT, 0);
+ r = usbd_sync_transfer(reqh);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(1, ("urioread: error=%d\n", r));
+ usbd_clear_endpoint_stall(sc->sc_pipeh_in);
+ tn = 0;
+ error = EIO;
+ break;
+ }
+ usbd_get_xfer_status(reqh, 0, 0, &tn, 0);
+
+ DPRINTFN(1, ("urioread: got %d bytes\n", tn));
+ error = uiomove(buf, tn, uio);
+ if (error || tn < n)
+ break;
+ }
+ usbd_free_xfer(reqh);
+ return error;
+}
+
+int
+uriowrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct urio_softc * sc;
+ usbd_xfer_handle reqh;
+ int unit = URIOUNIT(dev);
+ usbd_status r;
+ char buf[URIO_BBSIZE];
+ u_int32_t n;
+ int error = 0;
+
+ sc = devclass_get_softc(urio_devclass, unit);
+ DPRINTFN(5, ("uriowrite: %d\n", unit));
+ if (!sc->sc_opened)
+ return EIO;
+
+ sc->sc_refcnt++;
+ reqh = usbd_alloc_xfer(sc->sc_udev);
+ if (reqh == 0)
+ return EIO;
+ while ((n = min(URIO_BBSIZE, uio->uio_resid)) != 0) {
+ error = uiomove(buf, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("uriowrite: transfer %d bytes\n", n));
+ usbd_setup_xfer(reqh, sc->sc_pipeh_out, 0, buf, n,
+ 0, RIO_RW_TIMEOUT, 0);
+ r = usbd_sync_transfer(reqh);
+ if (r != USBD_NORMAL_COMPLETION) {
+ DPRINTFN(1, ("uriowrite: error=%d\n", r));
+ usbd_clear_endpoint_stall(sc->sc_pipeh_out);
+ error = EIO;
+ break;
+ }
+ usbd_get_xfer_status(reqh, 0, 0, 0, 0);
+ }
+
+ usbd_free_xfer(reqh);
+ return error;
+}
+
+
+int
+urioioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *p)
+{
+ struct urio_softc * sc;
+ int unit = URIOUNIT(dev);
+ struct RioCommand *rio_cmd;
+ int requesttype, len;
+ struct iovec iov;
+ struct uio uio;
+ usb_device_request_t req;
+ int req_flags = 0, req_actlen = 0;
+ void *ptr = 0;
+ int error = 0;
+ usbd_status r;
+
+ sc = devclass_get_softc(urio_devclass, unit);
+ switch (cmd) {
+ case RIO_RECV_COMMAND:
+ if (!(flag & FWRITE))
+ return EPERM;
+ rio_cmd = (struct RioCommand *)addr;
+ if (rio_cmd == NULL)
+ return EINVAL;
+ len = rio_cmd->length;
+
+ requesttype = rio_cmd->requesttype | UT_READ_VENDOR_DEVICE;
+ DPRINTFN(1,("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
+ requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len));
+ break;
+
+ case RIO_SEND_COMMAND:
+ if (!(flag & FWRITE))
+ return EPERM;
+ rio_cmd = (struct RioCommand *)addr;
+ if (rio_cmd == NULL)
+ return EINVAL;
+ len = rio_cmd->length;
+
+ requesttype = rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE;
+ DPRINTFN(1,("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
+ requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len));
+ break;
+
+ default:
+ return EINVAL;
+ break;
+ }
+
+ /* Send rio control message */
+ req.bmRequestType = requesttype;
+ req.bRequest = rio_cmd->request;
+ USETW(req.wValue, rio_cmd->value);
+ USETW(req.wIndex, rio_cmd->index);
+ USETW(req.wLength, len);
+
+ if (len < 0 || len > 32767)
+ return EINVAL;
+ if (len != 0) {
+ iov.iov_base = (caddr_t)rio_cmd->buffer;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_rw =
+ req.bmRequestType & UT_READ ?
+ UIO_READ : UIO_WRITE;
+ uio.uio_td = p;
+ ptr = malloc(len, M_TEMP, M_WAITOK);
+ if (uio.uio_rw == UIO_WRITE) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+
+ r = usbd_do_request_flags(sc->sc_udev, &req,
+ ptr, req_flags, &req_actlen,
+ USBD_DEFAULT_TIMEOUT);
+ if (r == USBD_NORMAL_COMPLETION) {
+ error = 0;
+ if (len != 0) {
+ if (uio.uio_rw == UIO_READ) {
+ error = uiomove(ptr, len, &uio);
+ }
+ }
+ } else {
+ error = EIO;
+ }
+
+ret:
+ if (ptr)
+ free(ptr, M_TEMP);
+ return error;
+}
+
+static int
+urio_detach(device_t self)
+{
+ struct urio_softc *sc = device_get_softc(self);
+ int s;
+
+ DPRINTF(("urio_detach: sc=%p\n", sc));
+ sc->sc_dying = 1;
+ if (sc->sc_pipeh_in)
+ usbd_abort_pipe(sc->sc_pipeh_in);
+
+ if (sc->sc_pipeh_out)
+ usbd_abort_pipe(sc->sc_pipeh_out);
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(sc->sc_dev);
+ }
+ splx(s);
+
+ destroy_dev(sc->sc_dev_t);
+ /* XXX not implemented yet */
+ device_set_desc(self, NULL);
+ return 0;
+}
+
+MODULE_DEPEND(uscanner, usb, 1, 1, 1);
+DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/usb.c b/sys/legacy/dev/usb/usb.c
new file mode 100644
index 0000000..b1b7d44
--- /dev/null
+++ b/sys/legacy/dev/usb/usb.c
@@ -0,0 +1,939 @@
+/* $NetBSD: usb.c,v 1.68 2002/02/20 20:30:12 christos Exp $ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: usb.c,v 1.70 2002/05/09 21:54:32 augustss Exp $
+ * $NetBSD: usb.c,v 1.71 2002/06/01 23:51:04 lukem Exp $
+ * $NetBSD: usb.c,v 1.73 2002/09/23 05:51:19 simonb Exp $
+ * $NetBSD: usb.c,v 1.80 2003/11/07 17:03:25 wiz Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * 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.
+ */
+
+/*
+ * USB specifications and other documentation can be found at
+ * http://www.usb.org/developers/docs/ and
+ * http://www.usb.org/developers/devclass_docs/
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/unistd.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/uio.h>
+#include <sys/kthread.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+#include <sys/signalvar.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#define USBUNIT(d) (dev2unit(d)) /* usb_discover device nodes, kthread */
+#define USB_DEV_MINOR 255 /* event queue device */
+
+MALLOC_DEFINE(M_USB, "USB", "USB");
+MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
+MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
+
+#include "usb_if.h"
+
+#include <machine/bus.h>
+
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_quirks.h>
+
+/* Define this unconditionally in case a kernel module is loaded that
+ * has been compiled with debugging options.
+ */
+SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging");
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+int usbdebug = 0;
+SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW,
+ &usbdebug, 0, "usb debug level");
+/*
+ * 0 - do usual exploration
+ * 1 - do not use timeout exploration
+ * >1 - do no exploration
+ */
+int usb_noexplore = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct usb_softc {
+ device_t sc_dev; /* base device */
+ struct cdev *sc_usbdev; /* /dev/usbN device */
+ TAILQ_ENTRY(usb_softc) sc_coldexplist; /* cold needs-explore list */
+ usbd_bus_handle sc_bus; /* USB controller */
+ struct usbd_port sc_port; /* dummy port for root hub */
+
+ struct proc *sc_event_thread;
+
+ char sc_dying;
+};
+
+struct usb_taskq {
+ TAILQ_HEAD(, usb_task) tasks;
+ struct proc *task_thread_proc;
+ const char *name;
+ int taskcreated; /* task thread exists. */
+};
+
+static struct usb_taskq usb_taskq[USB_NUM_TASKQS];
+
+d_open_t usbopen;
+d_close_t usbclose;
+d_read_t usbread;
+d_ioctl_t usbioctl;
+d_poll_t usbpoll;
+
+struct cdevsw usb_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = usbopen,
+ .d_close = usbclose,
+ .d_read = usbread,
+ .d_ioctl = usbioctl,
+ .d_poll = usbpoll,
+ .d_name = "usb",
+};
+
+static void usb_discover(void *);
+static void usb_create_event_thread(void *);
+static void usb_event_thread(void *);
+static void usb_task_thread(void *);
+
+static struct cdev *usb_dev; /* The /dev/usb device. */
+static int usb_ndevs; /* Number of /dev/usbN devices. */
+/* Busses to explore at the end of boot-time device configuration. */
+static TAILQ_HEAD(, usb_softc) usb_coldexplist =
+ TAILQ_HEAD_INITIALIZER(usb_coldexplist);
+
+#define USB_MAX_EVENTS 100
+struct usb_event_q {
+ struct usb_event ue;
+ TAILQ_ENTRY(usb_event_q) next;
+};
+static TAILQ_HEAD(, usb_event_q) usb_events =
+ TAILQ_HEAD_INITIALIZER(usb_events);
+static int usb_nevents = 0;
+static struct selinfo usb_selevent;
+static struct proc *usb_async_proc; /* process that wants USB SIGIO */
+static int usb_dev_open = 0;
+static void usb_add_event(int, struct usb_event *);
+
+static int usb_get_next_event(struct usb_event *);
+
+static const char *usbrev_str[] = USBREV_STR;
+
+static device_probe_t usb_match;
+static device_attach_t usb_attach;
+static device_detach_t usb_detach;
+static bus_child_detached_t usb_child_detached;
+
+static device_method_t usb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, usb_match),
+ DEVMETHOD(device_attach, usb_attach),
+ DEVMETHOD(device_detach, usb_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_child_detached, usb_child_detached),
+
+ { 0, 0 }
+};
+
+static driver_t usb_driver = {
+ "usb",
+ usb_methods,
+ sizeof(struct usb_softc)
+};
+
+static devclass_t usb_devclass;
+
+DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usb, ehci, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usb, slhci, usb_driver, usb_devclass, 0, 0);
+MODULE_VERSION(usb, 1);
+
+static int
+usb_match(device_t self)
+{
+ DPRINTF(("usbd_match\n"));
+ return (UMATCH_GENERIC);
+}
+
+static int
+usb_attach(device_t self)
+{
+ struct usb_softc *sc = device_get_softc(self);
+ void *aux = device_get_ivars(self);
+ usbd_device_handle dev;
+ usbd_status err;
+ int usbrev;
+ int speed;
+ struct usb_event ue;
+
+ sc->sc_dev = self;
+
+ DPRINTF(("usbd_attach\n"));
+
+ usbd_init();
+ sc->sc_bus = aux;
+ sc->sc_bus->usbctl = sc;
+ sc->sc_port.power = USB_MAX_POWER;
+
+ printf("%s", device_get_nameunit(sc->sc_dev));
+ usbrev = sc->sc_bus->usbrev;
+ printf(": USB revision %s", usbrev_str[usbrev]);
+ switch (usbrev) {
+ case USBREV_1_0:
+ case USBREV_1_1:
+ speed = USB_SPEED_FULL;
+ break;
+ case USBREV_2_0:
+ speed = USB_SPEED_HIGH;
+ break;
+ default:
+ printf(", not supported\n");
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+ printf("\n");
+
+ /* Make sure not to use tsleep() if we are cold booting. */
+ if (cold)
+ sc->sc_bus->use_polling++;
+
+ ue.u.ue_ctrlr.ue_bus = device_get_unit(sc->sc_dev);
+ usb_add_event(USB_EVENT_CTRLR_ATTACH, &ue);
+
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ /* XXX we should have our own level */
+ sc->sc_bus->soft = softintr_establish(IPL_SOFTNET,
+ sc->sc_bus->methods->soft_intr, sc->sc_bus);
+ if (sc->sc_bus->soft == NULL) {
+ printf("%s: can't register softintr\n", device_get_nameunit(sc->sc_dev));
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+#else
+ usb_callout_init(sc->sc_bus->softi);
+#endif
+#endif
+
+ err = usbd_new_device(sc->sc_dev, sc->sc_bus, 0, speed, 0,
+ &sc->sc_port);
+ if (!err) {
+ dev = sc->sc_port.device;
+ if (dev->hub == NULL) {
+ sc->sc_dying = 1;
+ printf("%s: root device is not a hub\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+ sc->sc_bus->root_hub = dev;
+#if 1
+ /*
+ * Turning this code off will delay attachment of USB devices
+ * until the USB event thread is running, which means that
+ * the keyboard will not work until after cold boot.
+ */
+ if (cold) {
+ /* Explore high-speed busses before others. */
+ if (speed == USB_SPEED_HIGH)
+ dev->hub->explore(sc->sc_bus->root_hub);
+ else
+ TAILQ_INSERT_TAIL(&usb_coldexplist, sc,
+ sc_coldexplist);
+ }
+#endif
+ } else {
+ printf("%s: root hub problem, error=%d\n",
+ device_get_nameunit(sc->sc_dev), err);
+ sc->sc_dying = 1;
+ }
+ if (cold)
+ sc->sc_bus->use_polling--;
+
+ /* XXX really do right config_pending_incr(); */
+ usb_create_event_thread(sc);
+ /* The per controller devices (used for usb_discover) */
+ /* XXX This is redundant now, but old usbd's will want it */
+ sc->sc_usbdev = make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT,
+ GID_OPERATOR, 0660, "usb%d", device_get_unit(self));
+ if (usb_ndevs++ == 0) {
+ /* The device spitting out events */
+ usb_dev = make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT,
+ GID_OPERATOR, 0660, "usb");
+ }
+ return 0;
+}
+
+static const char *taskq_names[] = USB_TASKQ_NAMES;
+
+void
+usb_create_event_thread(void *arg)
+{
+ struct usb_softc *sc = arg;
+ struct usb_taskq *taskq;
+ int i;
+
+ if (kproc_create(usb_event_thread, sc, &sc->sc_event_thread,
+ RFHIGHPID, 0, device_get_nameunit(sc->sc_dev))) {
+ printf("%s: unable to create event thread for\n",
+ device_get_nameunit(sc->sc_dev));
+ panic("usb_create_event_thread");
+ }
+ for (i = 0; i < USB_NUM_TASKQS; i++) {
+ taskq = &usb_taskq[i];
+
+ if (taskq->taskcreated == 0) {
+ taskq->taskcreated = 1;
+ taskq->name = taskq_names[i];
+ TAILQ_INIT(&taskq->tasks);
+ if (kproc_create(usb_task_thread, taskq,
+ &taskq->task_thread_proc, RFHIGHPID, 0,
+ taskq->name)) {
+ printf("unable to create task thread\n");
+ panic("usb_create_event_thread task");
+ }
+ }
+ }
+}
+
+/*
+ * Add a task to be performed by the task thread. This function can be
+ * called from any context and the task will be executed in a process
+ * context ASAP.
+ */
+void
+usb_add_task(usbd_device_handle dev, struct usb_task *task, int queue)
+{
+ struct usb_taskq *taskq;
+ int s;
+
+ s = splusb();
+ taskq = &usb_taskq[queue];
+ if (task->queue == -1) {
+ DPRINTFN(2,("usb_add_task: task=%p\n", task));
+ TAILQ_INSERT_TAIL(&taskq->tasks, task, next);
+ task->queue = queue;
+ } else {
+ DPRINTFN(3,("usb_add_task: task=%p on q\n", task));
+ }
+ wakeup(&taskq->tasks);
+ splx(s);
+}
+
+void
+usb_rem_task(usbd_device_handle dev, struct usb_task *task)
+{
+ struct usb_taskq *taskq;
+ int s;
+
+ s = splusb();
+ if (task->queue != -1) {
+ taskq = &usb_taskq[task->queue];
+ TAILQ_REMOVE(&taskq->tasks, task, next);
+ task->queue = -1;
+ }
+ splx(s);
+}
+
+void
+usb_event_thread(void *arg)
+{
+ static int newthread_wchan;
+ struct usb_softc *sc = arg;
+
+ mtx_lock(&Giant);
+
+ DPRINTF(("usb_event_thread: start\n"));
+
+ /*
+ * In case this controller is a companion controller to an
+ * EHCI controller we need to wait until the EHCI controller
+ * has grabbed the port. What we do here is wait until no new
+ * USB threads have been created in a while. XXX we actually
+ * just want to wait for the PCI slot to be fully scanned.
+ *
+ * Note that when you `kldload usb' it actually attaches the
+ * devices in order that the drivers appear in the kld, not the
+ * normal PCI order, since the addition of each driver within
+ * usb.ko (ohci, ehci etc.) causes a separate PCI bus re-scan.
+ */
+ wakeup(&newthread_wchan);
+ for (;;) {
+ if (tsleep(&newthread_wchan , PWAIT, "usbets", hz * 4) != 0)
+ break;
+ }
+
+ /* Make sure first discover does something. */
+ sc->sc_bus->needs_explore = 1;
+ usb_discover(sc);
+ /* XXX really do right config_pending_decr(); */
+
+ while (!sc->sc_dying) {
+#ifdef USB_DEBUG
+ if (usb_noexplore < 2)
+#endif
+ usb_discover(sc);
+#ifdef USB_DEBUG
+ (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt",
+ usb_noexplore ? 0 : hz * 60);
+#else
+ (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt",
+ hz * 60);
+#endif
+ DPRINTFN(2,("usb_event_thread: woke up\n"));
+ }
+ sc->sc_event_thread = NULL;
+
+ /* In case parent is waiting for us to exit. */
+ wakeup(sc);
+
+ DPRINTF(("usb_event_thread: exit\n"));
+ while (mtx_owned(&Giant))
+ mtx_unlock(&Giant);
+ kproc_exit(0);
+}
+
+void
+usb_task_thread(void *arg)
+{
+ struct usb_task *task;
+ struct usb_taskq *taskq;
+ int s;
+
+ mtx_lock(&Giant);
+
+ taskq = arg;
+ DPRINTF(("usb_task_thread: start taskq %s\n", taskq->name));
+
+ s = splusb();
+ while (usb_ndevs > 0) {
+ task = TAILQ_FIRST(&taskq->tasks);
+ if (task == NULL) {
+ tsleep(&taskq->tasks, PWAIT, "usbtsk", 0);
+ task = TAILQ_FIRST(&taskq->tasks);
+ }
+ DPRINTFN(2,("usb_task_thread: woke up task=%p\n", task));
+ if (task != NULL) {
+ TAILQ_REMOVE(&taskq->tasks, task, next);
+ task->queue = -1;
+ splx(s);
+ task->fun(task->arg);
+ s = splusb();
+ }
+ }
+ splx(s);
+
+ taskq->taskcreated = 0;
+ wakeup(&taskq->taskcreated);
+
+ DPRINTF(("usb_event_thread: exit\n"));
+ while (mtx_owned(&Giant))
+ mtx_unlock(&Giant);
+ kproc_exit(0);
+}
+
+int
+usbopen(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ int unit = USBUNIT(dev);
+ struct usb_softc *sc;
+
+ if (unit == USB_DEV_MINOR) {
+ if (usb_dev_open)
+ return (EBUSY);
+ usb_dev_open = 1;
+ usb_async_proc = 0;
+ return (0);
+ }
+ sc = devclass_get_softc(usb_devclass, unit);
+ if (sc == NULL)
+ return (ENXIO);
+ if (sc->sc_dying)
+ return (EIO);
+
+ return (0);
+}
+
+int
+usbread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct usb_event ue;
+ int unit = USBUNIT(dev);
+ int s, error, n;
+
+ if (unit != USB_DEV_MINOR)
+ return (ENODEV);
+
+ if (uio->uio_resid != sizeof(struct usb_event))
+ return (EINVAL);
+
+ error = 0;
+ s = splusb();
+ for (;;) {
+ n = usb_get_next_event(&ue);
+ if (n != 0)
+ break;
+ if (flag & O_NONBLOCK) {
+ error = EWOULDBLOCK;
+ break;
+ }
+ error = tsleep(&usb_events, PZERO | PCATCH, "usbrea", 0);
+ if (error)
+ break;
+ }
+ splx(s);
+ if (!error)
+ error = uiomove((void *)&ue, uio->uio_resid, uio);
+
+ return (error);
+}
+
+int
+usbclose(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ int unit = USBUNIT(dev);
+
+ if (unit == USB_DEV_MINOR) {
+ usb_async_proc = 0;
+ usb_dev_open = 0;
+ }
+
+ return (0);
+}
+
+int
+usbioctl(struct cdev *devt, u_long cmd, caddr_t data, int flag, struct thread *p)
+{
+ struct usb_softc *sc;
+ int unit = USBUNIT(devt);
+
+ if (unit == USB_DEV_MINOR) {
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ return (0);
+
+ case FIOASYNC:
+ if (*(int *)data)
+ usb_async_proc = p->td_proc;
+ else
+ usb_async_proc = 0;
+ return (0);
+
+ default:
+ return (EINVAL);
+ }
+ }
+ sc = devclass_get_softc(usb_devclass, unit);
+ if (sc->sc_dying)
+ return (EIO);
+
+ switch (cmd) {
+ /* This part should be deleted */
+ case USB_DISCOVER:
+ break;
+ case USB_REQUEST:
+ {
+ struct usb_ctl_request *ur = (void *)data;
+ int len = UGETW(ur->ucr_request.wLength);
+ struct iovec iov;
+ struct uio uio;
+ void *ptr = 0;
+ int addr = ur->ucr_addr;
+ usbd_status err;
+ int error = 0;
+
+ DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len));
+ if (len < 0 || len > 32768)
+ return (EINVAL);
+ if (addr < 0 || addr >= USB_MAX_DEVICES ||
+ sc->sc_bus->devices[addr] == 0)
+ return (EINVAL);
+ if (len != 0) {
+ iov.iov_base = (caddr_t)ur->ucr_data;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_rw =
+ ur->ucr_request.bmRequestType & UT_READ ?
+ UIO_READ : UIO_WRITE;
+ uio.uio_td = p;
+ ptr = malloc(len, M_TEMP, M_WAITOK);
+ if (uio.uio_rw == UIO_WRITE) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ err = usbd_do_request_flags(sc->sc_bus->devices[addr],
+ &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen,
+ USBD_DEFAULT_TIMEOUT);
+ if (err) {
+ error = EIO;
+ goto ret;
+ }
+ if (len != 0) {
+ if (uio.uio_rw == UIO_READ) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ ret:
+ if (ptr)
+ free(ptr, M_TEMP);
+ return (error);
+ }
+
+ case USB_DEVICEINFO:
+ {
+ struct usb_device_info *di = (void *)data;
+ int addr = di->udi_addr;
+ usbd_device_handle dev;
+
+ if (addr < 1 || addr >= USB_MAX_DEVICES)
+ return (EINVAL);
+ dev = sc->sc_bus->devices[addr];
+ if (dev == NULL)
+ return (ENXIO);
+ usbd_fill_deviceinfo(dev, di, 1);
+ break;
+ }
+
+ case USB_DEVICESTATS:
+ *(struct usb_device_stats *)data = sc->sc_bus->stats;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+usbpoll(struct cdev *dev, int events, struct thread *p)
+{
+ int revents, mask, s;
+ int unit = USBUNIT(dev);
+
+ if (unit == USB_DEV_MINOR) {
+ revents = 0;
+ mask = POLLIN | POLLRDNORM;
+
+ s = splusb();
+ if (events & mask && usb_nevents > 0)
+ revents |= events & mask;
+ if (revents == 0 && events & mask)
+ selrecord(p, &usb_selevent);
+ splx(s);
+
+ return (revents);
+ } else {
+ return (0); /* select/poll never wakes up - back compat */
+ }
+}
+
+/* Explore device tree from the root. */
+static void
+usb_discover(void *v)
+{
+ struct usb_softc *sc = v;
+
+ /* splxxx should be changed to mutexes for preemption safety some day */
+ int s;
+
+ DPRINTFN(2,("usb_discover\n"));
+#ifdef USB_DEBUG
+ if (usb_noexplore > 1)
+ return;
+#endif
+
+ /*
+ * We need mutual exclusion while traversing the device tree,
+ * but this is guaranteed since this function is only called
+ * from the event thread for the controller.
+ */
+ s = splusb();
+ while (sc->sc_bus->needs_explore && !sc->sc_dying) {
+ sc->sc_bus->needs_explore = 0;
+ splx(s);
+ sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
+ s = splusb();
+ }
+ splx(s);
+}
+
+void
+usb_needs_explore(usbd_device_handle dev)
+{
+ DPRINTFN(2,("usb_needs_explore\n"));
+ dev->bus->needs_explore = 1;
+ wakeup(&dev->bus->needs_explore);
+}
+
+/* Called at splusb() */
+int
+usb_get_next_event(struct usb_event *ue)
+{
+ struct usb_event_q *ueq;
+
+ if (usb_nevents <= 0)
+ return (0);
+ ueq = TAILQ_FIRST(&usb_events);
+#ifdef DIAGNOSTIC
+ if (ueq == NULL) {
+ printf("usb: usb_nevents got out of sync! %d\n", usb_nevents);
+ usb_nevents = 0;
+ return (0);
+ }
+#endif
+ *ue = ueq->ue;
+ TAILQ_REMOVE(&usb_events, ueq, next);
+ free(ueq, M_USBDEV);
+ usb_nevents--;
+ return (1);
+}
+
+void
+usbd_add_dev_event(int type, usbd_device_handle udev)
+{
+ struct usb_event ue;
+
+ usbd_fill_deviceinfo(udev, &ue.u.ue_device, USB_EVENT_IS_ATTACH(type));
+ usb_add_event(type, &ue);
+}
+
+void
+usbd_add_drv_event(int type, usbd_device_handle udev, device_t dev)
+{
+ struct usb_event ue;
+
+ ue.u.ue_driver.ue_cookie = udev->cookie;
+ strncpy(ue.u.ue_driver.ue_devname, device_get_nameunit(dev),
+ sizeof ue.u.ue_driver.ue_devname);
+ usb_add_event(type, &ue);
+}
+
+void
+usb_add_event(int type, struct usb_event *uep)
+{
+ struct usb_event_q *ueq;
+ struct usb_event ue;
+ struct timeval thetime;
+ int s;
+
+ ueq = malloc(sizeof *ueq, M_USBDEV, M_WAITOK);
+ ueq->ue = *uep;
+ ueq->ue.ue_type = type;
+ microtime(&thetime);
+ TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time);
+
+ s = splusb();
+ if (USB_EVENT_IS_DETACH(type)) {
+ struct usb_event_q *ueqi, *ueqi_next;
+
+ for (ueqi = TAILQ_FIRST(&usb_events); ueqi; ueqi = ueqi_next) {
+ ueqi_next = TAILQ_NEXT(ueqi, next);
+ if (ueqi->ue.u.ue_driver.ue_cookie.cookie ==
+ uep->u.ue_device.udi_cookie.cookie) {
+ TAILQ_REMOVE(&usb_events, ueqi, next);
+ free(ueqi, M_USBDEV);
+ usb_nevents--;
+ ueqi_next = TAILQ_FIRST(&usb_events);
+ }
+ }
+ }
+ if (usb_nevents >= USB_MAX_EVENTS) {
+ /* Too many queued events, drop an old one. */
+ DPRINTF(("usb: event dropped\n"));
+ (void)usb_get_next_event(&ue);
+ }
+ TAILQ_INSERT_TAIL(&usb_events, ueq, next);
+ usb_nevents++;
+ wakeup(&usb_events);
+ selwakeuppri(&usb_selevent, PZERO);
+ if (usb_async_proc != NULL) {
+ PROC_LOCK(usb_async_proc);
+ psignal(usb_async_proc, SIGIO);
+ PROC_UNLOCK(usb_async_proc);
+ }
+ splx(s);
+}
+
+void
+usb_schedsoftintr(usbd_bus_handle bus)
+{
+ DPRINTFN(10,("usb_schedsoftintr: polling=%d\n", bus->use_polling));
+#ifdef USB_USE_SOFTINTR
+ if (bus->use_polling) {
+ bus->methods->soft_intr(bus);
+ } else {
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ softintr_schedule(bus->soft);
+#else
+ if (!callout_pending(&bus->softi))
+ callout_reset(&bus->softi, 0, bus->methods->soft_intr,
+ bus);
+#endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */
+ }
+#else
+ bus->methods->soft_intr(bus);
+#endif /* USB_USE_SOFTINTR */
+}
+
+static int
+usb_detach(device_t self)
+{
+ struct usb_softc *sc = device_get_softc(self);
+ struct usb_event ue;
+ struct usb_taskq *taskq;
+ int i;
+
+ DPRINTF(("usb_detach: start\n"));
+
+ sc->sc_dying = 1;
+
+ /* Make all devices disconnect. */
+ if (sc->sc_port.device != NULL)
+ usb_disconnect_port(&sc->sc_port, self);
+
+ /* Kill off event thread. */
+ if (sc->sc_event_thread != NULL) {
+ wakeup(&sc->sc_bus->needs_explore);
+ if (tsleep(sc, PWAIT, "usbdet", hz * 60))
+ printf("%s: event thread didn't die\n",
+ device_get_nameunit(sc->sc_dev));
+ DPRINTF(("usb_detach: event thread dead\n"));
+ }
+
+ destroy_dev(sc->sc_usbdev);
+ if (--usb_ndevs == 0) {
+ destroy_dev(usb_dev);
+ usb_dev = NULL;
+ for (i = 0; i < USB_NUM_TASKQS; i++) {
+ taskq = &usb_taskq[i];
+ wakeup(&taskq->tasks);
+ if (tsleep(&taskq->taskcreated, PWAIT, "usbtdt",
+ hz * 60)) {
+ printf("usb task thread %s didn't die\n",
+ taskq->name);
+ }
+ }
+ }
+
+ usbd_finish();
+
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ if (sc->sc_bus->soft != NULL) {
+ softintr_disestablish(sc->sc_bus->soft);
+ sc->sc_bus->soft = NULL;
+ }
+#else
+ callout_stop(&sc->sc_bus->softi);
+#endif
+#endif
+
+ ue.u.ue_ctrlr.ue_bus = device_get_unit(sc->sc_dev);
+ usb_add_event(USB_EVENT_CTRLR_DETACH, &ue);
+
+ return (0);
+}
+
+static void
+usb_child_detached(device_t self, device_t child)
+{
+ struct usb_softc *sc = device_get_softc(self);
+
+ /* XXX, should check it is the right device. */
+ sc->sc_port.device = NULL;
+}
+
+/* Explore USB busses at the end of device configuration. */
+static void
+usb_cold_explore(void *arg)
+{
+ struct usb_softc *sc;
+
+ KASSERT(cold || TAILQ_EMPTY(&usb_coldexplist),
+ ("usb_cold_explore: busses to explore when !cold"));
+ while (!TAILQ_EMPTY(&usb_coldexplist)) {
+ sc = TAILQ_FIRST(&usb_coldexplist);
+ TAILQ_REMOVE(&usb_coldexplist, sc, sc_coldexplist);
+
+ sc->sc_bus->use_polling++;
+ sc->sc_port.device->hub->explore(sc->sc_bus->root_hub);
+ sc->sc_bus->use_polling--;
+ }
+}
+
+SYSINIT(usb_cold_explore, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE,
+ usb_cold_explore, NULL);
diff --git a/sys/legacy/dev/usb/usb.h b/sys/legacy/dev/usb/usb.h
new file mode 100644
index 0000000..7e64cac
--- /dev/null
+++ b/sys/legacy/dev/usb/usb.h
@@ -0,0 +1,708 @@
+/* $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#if defined(_KERNEL)
+#include "opt_usb.h"
+
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_hw_usb);
+#endif
+
+#include <sys/malloc.h>
+
+MALLOC_DECLARE(M_USB);
+MALLOC_DECLARE(M_USBDEV);
+MALLOC_DECLARE(M_USBHC);
+#endif /* _KERNEL */
+
+#define PWR_RESUME 0
+#define PWR_SUSPEND 1
+#define PWR_STANDBY 2
+#define PWR_SOFTSUSPEND 3
+#define PWR_SOFTSTANDBY 4
+#define PWR_SOFTRESUME 5
+
+/* These two defines are used by usbd to autoload the usb kld */
+#define USB_KLD "usb" /* name of usb module */
+#define USB_UHUB "usb/uhub" /* root hub */
+
+#define USB_STACK_VERSION 2
+
+#define USB_MAX_DEVICES 128
+#define USB_START_ADDR 0
+
+#define USB_CONTROL_ENDPOINT 0
+#define USB_MAX_ENDPOINTS 16
+
+#define USB_FRAMES_PER_SECOND 1000
+
+/*
+ * The USB records contain some unaligned little-endian word
+ * components. The U[SG]ETW macros take care of both the alignment
+ * and endian problem and should always be used to access non-byte
+ * values.
+ */
+typedef u_int8_t uByte;
+typedef u_int8_t uWord[2];
+typedef u_int8_t uDWord[4];
+
+#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h))
+
+#if 1
+#define UGETW(w) ((w)[0] | ((w)[1] << 8))
+#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8))
+#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24))
+#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \
+ (w)[1] = (u_int8_t)((v) >> 8), \
+ (w)[2] = (u_int8_t)((v) >> 16), \
+ (w)[3] = (u_int8_t)((v) >> 24))
+#else
+/*
+ * On little-endian machines that can handle unanliged accesses
+ * (e.g. i386) these macros can be replaced by the following.
+ */
+#define UGETW(w) (*(u_int16_t *)(w))
+#define USETW(w,v) (*(u_int16_t *)(w) = (v))
+#define UGETDW(w) (*(u_int32_t *)(w))
+#define USETDW(w,v) (*(u_int32_t *)(w) = (v))
+#endif
+
+#define UPACKED __packed
+
+typedef struct {
+ uByte bmRequestType;
+ uByte bRequest;
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+} UPACKED usb_device_request_t;
+
+#define UT_WRITE 0x00
+#define UT_READ 0x80
+#define UT_STANDARD 0x00
+#define UT_CLASS 0x20
+#define UT_VENDOR 0x40
+#define UT_DEVICE 0x00
+#define UT_INTERFACE 0x01
+#define UT_ENDPOINT 0x02
+#define UT_OTHER 0x03
+
+#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE)
+#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE)
+#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT)
+#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE)
+#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE)
+#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT)
+#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE)
+#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE)
+#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER)
+#define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT)
+#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE)
+#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE)
+#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER)
+#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT)
+#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE)
+#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE)
+#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER)
+#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT)
+#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE)
+#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE)
+#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER)
+#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT)
+
+/* Requests */
+#define UR_GET_STATUS 0x00
+#define UR_CLEAR_FEATURE 0x01
+#define UR_SET_FEATURE 0x03
+#define UR_SET_ADDRESS 0x05
+#define UR_GET_DESCRIPTOR 0x06
+#define UDESC_DEVICE 0x01
+#define UDESC_CONFIG 0x02
+#define UDESC_STRING 0x03
+#define UDESC_INTERFACE 0x04
+#define UDESC_ENDPOINT 0x05
+#define UDESC_DEVICE_QUALIFIER 0x06
+#define UDESC_OTHER_SPEED_CONFIGURATION 0x07
+#define UDESC_INTERFACE_POWER 0x08
+#define UDESC_OTG 0x09
+#define UDESC_CS_DEVICE 0x21 /* class specific */
+#define UDESC_CS_CONFIG 0x22
+#define UDESC_CS_STRING 0x23
+#define UDESC_CS_INTERFACE 0x24
+#define UDESC_CS_ENDPOINT 0x25
+#define UDESC_HUB 0x29
+#define UR_SET_DESCRIPTOR 0x07
+#define UR_GET_CONFIG 0x08
+#define UR_SET_CONFIG 0x09
+#define UR_GET_INTERFACE 0x0a
+#define UR_SET_INTERFACE 0x0b
+#define UR_SYNCH_FRAME 0x0c
+
+/* Feature numbers */
+#define UF_ENDPOINT_HALT 0
+#define UF_DEVICE_REMOTE_WAKEUP 1
+#define UF_TEST_MODE 2
+
+#define USB_MAX_IPACKET 8 /* maximum size of the initial packet */
+
+#define USB_2_MAX_CTRL_PACKET 64
+#define USB_2_MAX_BULK_PACKET 512
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+} UPACKED usb_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+#define UD_USB_2_0 0x0200
+#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0)
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize;
+ /* The fields below are not part of the initial descriptor. */
+ uWord idVendor;
+ uWord idProduct;
+ uWord bcdDevice;
+ uByte iManufacturer;
+ uByte iProduct;
+ uByte iSerialNumber;
+ uByte bNumConfigurations;
+} UPACKED usb_device_descriptor_t;
+#define USB_DEVICE_DESCRIPTOR_SIZE 18
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumInterface;
+ uByte bConfigurationValue;
+ uByte iConfiguration;
+ uByte bmAttributes;
+#define UC_BUS_POWERED 0x80
+#define UC_SELF_POWERED 0x40
+#define UC_REMOTE_WAKEUP 0x20
+ uByte bMaxPower; /* max current in 2 mA units */
+#define UC_POWER_FACTOR 2
+} UPACKED usb_config_descriptor_t;
+#define USB_CONFIG_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bInterfaceNumber;
+ uByte bAlternateSetting;
+ uByte bNumEndpoints;
+ uByte bInterfaceClass;
+ uByte bInterfaceSubClass;
+ uByte bInterfaceProtocol;
+ uByte iInterface;
+} UPACKED usb_interface_descriptor_t;
+#define USB_INTERFACE_DESCRIPTOR_SIZE 9
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+#define UE_GET_DIR(a) ((a) & 0x80)
+#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7))
+#define UE_DIR_IN 0x80
+#define UE_DIR_OUT 0x00
+#define UE_ADDR 0x0f
+#define UE_GET_ADDR(a) ((a) & UE_ADDR)
+ uByte bmAttributes;
+#define UE_XFERTYPE 0x03
+#define UE_CONTROL 0x00
+#define UE_ISOCHRONOUS 0x01
+#define UE_BULK 0x02
+#define UE_INTERRUPT 0x03
+#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
+#define UE_ISO_TYPE 0x0c
+#define UE_ISO_ASYNC 0x04
+#define UE_ISO_ADAPT 0x08
+#define UE_ISO_SYNC 0x0c
+#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE)
+ uWord wMaxPacketSize;
+#define UE_GET_TRANS(a) (((a) >> 11) & 0x3)
+#define UE_GET_SIZE(a) ((a) & 0x7ff)
+ uByte bInterval;
+} UPACKED usb_endpoint_descriptor_t;
+#define USB_ENDPOINT_DESCRIPTOR_SIZE 7
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bString[127];
+} UPACKED usb_string_descriptor_t;
+#define USB_MAX_STRING_LEN 128
+#define USB_LANGUAGE_TABLE 0 /* # of the string language id table */
+
+/* Hub specific request */
+#define UR_GET_BUS_STATE 0x02
+#define UR_CLEAR_TT_BUFFER 0x08
+#define UR_RESET_TT 0x09
+#define UR_GET_TT_STATE 0x0a
+#define UR_STOP_TT 0x0b
+
+/* Hub features */
+#define UHF_C_HUB_LOCAL_POWER 0
+#define UHF_C_HUB_OVER_CURRENT 1
+#define UHF_PORT_CONNECTION 0
+#define UHF_PORT_ENABLE 1
+#define UHF_PORT_SUSPEND 2
+#define UHF_PORT_OVER_CURRENT 3
+#define UHF_PORT_RESET 4
+#define UHF_PORT_POWER 8
+#define UHF_PORT_LOW_SPEED 9
+#define UHF_C_PORT_CONNECTION 16
+#define UHF_C_PORT_ENABLE 17
+#define UHF_C_PORT_SUSPEND 18
+#define UHF_C_PORT_OVER_CURRENT 19
+#define UHF_C_PORT_RESET 20
+#define UHF_PORT_TEST 21
+#define UHF_PORT_INDICATOR 22
+
+typedef struct {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+#define UHD_PWR 0x0003
+#define UHD_PWR_GANGED 0x0000
+#define UHD_PWR_INDIVIDUAL 0x0001
+#define UHD_PWR_NO_SWITCH 0x0002
+#define UHD_COMPOUND 0x0004
+#define UHD_OC 0x0018
+#define UHD_OC_GLOBAL 0x0000
+#define UHD_OC_INDIVIDUAL 0x0008
+#define UHD_OC_NONE 0x0010
+#define UHD_TT_THINK 0x0060
+#define UHD_TT_THINK_8 0x0000
+#define UHD_TT_THINK_16 0x0020
+#define UHD_TT_THINK_24 0x0040
+#define UHD_TT_THINK_32 0x0060
+#define UHD_PORT_IND 0x0080
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+#define UHD_PWRON_FACTOR 2
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[32]; /* max 255 ports */
+#define UHD_NOT_REMOV(desc, i) \
+ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1)
+ /* deprecated */ uByte PortPowerCtrlMask[1];
+} UPACKED usb_hub_descriptor_t;
+#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize0;
+ uByte bNumConfigurations;
+ uByte bReserved;
+} UPACKED usb_device_qualifier_t;
+#define USB_DEVICE_QUALIFIER_SIZE 10
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bmAttributes;
+#define UOTG_SRP 0x01
+#define UOTG_HNP 0x02
+} UPACKED usb_otg_descriptor_t;
+
+/* OTG feature selectors */
+#define UOTG_B_HNP_ENABLE 3
+#define UOTG_A_HNP_SUPPORT 4
+#define UOTG_A_ALT_HNP_SUPPORT 5
+
+typedef struct {
+ uWord wStatus;
+/* Device status flags */
+#define UDS_SELF_POWERED 0x0001
+#define UDS_REMOTE_WAKEUP 0x0002
+/* Endpoint status flags */
+#define UES_HALT 0x0001
+} UPACKED usb_status_t;
+
+typedef struct {
+ uWord wHubStatus;
+#define UHS_LOCAL_POWER 0x0001
+#define UHS_OVER_CURRENT 0x0002
+ uWord wHubChange;
+} UPACKED usb_hub_status_t;
+
+typedef struct {
+ uWord wPortStatus;
+#define UPS_CURRENT_CONNECT_STATUS 0x0001
+#define UPS_PORT_ENABLED 0x0002
+#define UPS_SUSPEND 0x0004
+#define UPS_OVERCURRENT_INDICATOR 0x0008
+#define UPS_RESET 0x0010
+#define UPS_PORT_POWER 0x0100
+#define UPS_LOW_SPEED 0x0200
+#define UPS_HIGH_SPEED 0x0400
+#define UPS_PORT_TEST 0x0800
+#define UPS_PORT_INDICATOR 0x1000
+ uWord wPortChange;
+#define UPS_C_CONNECT_STATUS 0x0001
+#define UPS_C_PORT_ENABLED 0x0002
+#define UPS_C_SUSPEND 0x0004
+#define UPS_C_OVERCURRENT_INDICATOR 0x0008
+#define UPS_C_PORT_RESET 0x0010
+} UPACKED usb_port_status_t;
+
+/* Device class codes */
+#define UDCLASS_IN_INTERFACE 0x00
+#define UDCLASS_COMM 0x02
+#define UDCLASS_HUB 0x09
+#define UDSUBCLASS_HUB 0x00
+#define UDPROTO_FSHUB 0x00
+#define UDPROTO_HSHUBSTT 0x01
+#define UDPROTO_HSHUBMTT 0x02
+#define UDCLASS_DIAGNOSTIC 0xdc
+#define UDCLASS_WIRELESS 0xe0
+#define UDSUBCLASS_RF 0x01
+#define UDPROTO_BLUETOOTH 0x01
+#define UDCLASS_VENDOR 0xff
+
+/* Interface class codes */
+#define UICLASS_UNSPEC 0x00
+
+#define UICLASS_AUDIO 0x01
+#define UISUBCLASS_AUDIOCONTROL 1
+#define UISUBCLASS_AUDIOSTREAM 2
+#define UISUBCLASS_MIDISTREAM 3
+
+#define UICLASS_CDC 0x02 /* communication */
+#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1
+#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2
+#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3
+#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4
+#define UISUBCLASS_CAPI_CONTROLMODEL 5
+#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6
+#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7
+#define UIPROTO_CDC_AT 1
+
+#define UICLASS_HID 0x03
+#define UISUBCLASS_BOOT 1
+#define UIPROTO_BOOT_KEYBOARD 1
+#define UIPROTO_MOUSE 2
+
+#define UICLASS_PHYSICAL 0x05
+
+#define UICLASS_IMAGE 0x06
+
+#define UICLASS_PRINTER 0x07
+#define UISUBCLASS_PRINTER 1
+#define UIPROTO_PRINTER_UNI 1
+#define UIPROTO_PRINTER_BI 2
+#define UIPROTO_PRINTER_1284 3
+
+#define UICLASS_MASS 0x08
+#define UISUBCLASS_RBC 1
+#define UISUBCLASS_SFF8020I 2
+#define UISUBCLASS_QIC157 3
+#define UISUBCLASS_UFI 4
+#define UISUBCLASS_SFF8070I 5
+#define UISUBCLASS_SCSI 6
+#define UIPROTO_MASS_CBI_I 0
+#define UIPROTO_MASS_CBI 1
+#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */
+#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */
+
+#define UICLASS_HUB 0x09
+#define UISUBCLASS_HUB 0
+#define UIPROTO_FSHUB 0
+#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */
+#define UIPROTO_HSHUBMTT 1
+
+#define UICLASS_CDC_DATA 0x0a
+#define UISUBCLASS_DATA 0
+#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */
+#define UIPROTO_DATA_HDLC 0x31 /* HDLC */
+#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */
+#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */
+#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */
+#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */
+#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */
+#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */
+#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */
+#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */
+#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */
+#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/
+#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */
+
+#define UICLASS_SMARTCARD 0x0b
+
+/*#define UICLASS_FIRM_UPD 0x0c*/
+
+#define UICLASS_SECURITY 0x0d
+
+#define UICLASS_DIAGNOSTIC 0xdc
+
+#define UICLASS_WIRELESS 0xe0
+#define UISUBCLASS_RF 0x01
+#define UIPROTO_BLUETOOTH 0x01
+
+#define UICLASS_APPL_SPEC 0xfe
+#define UISUBCLASS_FIRMWARE_DOWNLOAD 1
+#define UISUBCLASS_IRDA 2
+#define UIPROTO_IRDA 0
+
+#define UICLASS_VENDOR 0xff
+#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
+#define UIPROTO_XBOX360_GAMEPAD 0x01
+
+
+#define USB_HUB_MAX_DEPTH 5
+
+/*
+ * Minimum time a device needs to be powered down to go through
+ * a power cycle. XXX Are these time in the spec?
+ */
+#define USB_POWER_DOWN_TIME 200 /* ms */
+#define USB_PORT_POWER_DOWN_TIME 100 /* ms */
+
+#if 0
+/* These are the values from the spec. */
+#define USB_PORT_RESET_DELAY 10 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */
+#define USB_PORT_RESET_RECOVERY 10 /* ms */
+#define USB_PORT_POWERUP_DELAY 100 /* ms */
+#define USB_SET_ADDRESS_SETTLE 2 /* ms */
+#define USB_RESUME_DELAY (20*5) /* ms */
+#define USB_RESUME_WAIT 10 /* ms */
+#define USB_RESUME_RECOVERY 10 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 0 /* ms */
+#else
+/* Allow for marginal (i.e. non-conforming) devices. */
+#define USB_PORT_RESET_DELAY 50 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */
+#define USB_PORT_RESET_RECOVERY 250 /* ms */
+#define USB_PORT_POWERUP_DELAY 300 /* ms */
+#define USB_SET_ADDRESS_SETTLE 10 /* ms */
+#define USB_RESUME_DELAY (50*5) /* ms */
+#define USB_RESUME_WAIT 50 /* ms */
+#define USB_RESUME_RECOVERY 50 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 20 /* ms */
+#endif
+
+#define USB_MIN_POWER 100 /* mA */
+#define USB_MAX_POWER 500 /* mA */
+
+#define USB_BUS_RESET_DELAY 100 /* ms XXX?*/
+
+
+#define USB_UNCONFIG_NO 0
+#define USB_UNCONFIG_INDEX (-1)
+
+/*** ioctl() related stuff ***/
+
+struct usb_ctl_request {
+ int ucr_addr;
+ usb_device_request_t ucr_request;
+ void *ucr_data;
+ int ucr_flags;
+#define USBD_SHORT_XFER_OK 0x04 /* allow short reads */
+ int ucr_actlen; /* actual length transferred */
+};
+
+struct usb_alt_interface {
+ int uai_config_index;
+ int uai_interface_index;
+ int uai_alt_no;
+};
+
+#define USB_CURRENT_CONFIG_INDEX (-1)
+#define USB_CURRENT_ALT_INDEX (-1)
+
+struct usb_config_desc {
+ int ucd_config_index;
+ usb_config_descriptor_t ucd_desc;
+};
+
+struct usb_interface_desc {
+ int uid_config_index;
+ int uid_interface_index;
+ int uid_alt_index;
+ usb_interface_descriptor_t uid_desc;
+};
+
+struct usb_endpoint_desc {
+ int ued_config_index;
+ int ued_interface_index;
+ int ued_alt_index;
+ int ued_endpoint_index;
+ usb_endpoint_descriptor_t ued_desc;
+};
+
+struct usb_full_desc {
+ int ufd_config_index;
+ u_int ufd_size;
+ u_char *ufd_data;
+};
+
+struct usb_string_desc {
+ int usd_string_index;
+ int usd_language_id;
+ usb_string_descriptor_t usd_desc;
+};
+
+struct usb_ctl_report_desc {
+ int ucrd_size;
+ u_char ucrd_data[1024]; /* filled data size will vary */
+};
+
+typedef struct { u_int32_t cookie; } usb_event_cookie_t;
+
+#define USB_MAX_DEVNAMES 4
+#define USB_MAX_DEVNAMELEN 16
+struct usb_device_info {
+ u_int8_t udi_bus;
+ u_int8_t udi_addr; /* device address */
+ usb_event_cookie_t udi_cookie;
+ char udi_product[USB_MAX_STRING_LEN];
+ char udi_vendor[USB_MAX_STRING_LEN];
+ char udi_release[8];
+ u_int16_t udi_productNo;
+ u_int16_t udi_vendorNo;
+ u_int16_t udi_releaseNo;
+ u_int8_t udi_class;
+ u_int8_t udi_subclass;
+ u_int8_t udi_protocol;
+ u_int8_t udi_config;
+ u_int8_t udi_speed;
+#define USB_SPEED_LOW 1
+#define USB_SPEED_FULL 2
+#define USB_SPEED_HIGH 3
+ int udi_power; /* power consumption in mA, 0 if selfpowered */
+ int udi_nports;
+ char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN];
+ u_int8_t udi_ports[16];/* hub only: addresses of devices on ports */
+#define USB_PORT_ENABLED 0xff
+#define USB_PORT_SUSPENDED 0xfe
+#define USB_PORT_POWERED 0xfd
+#define USB_PORT_DISABLED 0xfc
+};
+
+struct usb_ctl_report {
+ int ucr_report;
+ u_char ucr_data[1024]; /* filled data size will vary */
+};
+
+struct usb_device_stats {
+ u_long uds_requests[4]; /* indexed by transfer type UE_* */
+};
+
+/* Events that can be read from /dev/usb */
+struct usb_event {
+ int ue_type;
+#define USB_EVENT_CTRLR_ATTACH 1
+#define USB_EVENT_CTRLR_DETACH 2
+#define USB_EVENT_DEVICE_ATTACH 3
+#define USB_EVENT_DEVICE_DETACH 4
+#define USB_EVENT_DRIVER_ATTACH 5
+#define USB_EVENT_DRIVER_DETACH 6
+#define USB_EVENT_IS_ATTACH(n) ((n) == USB_EVENT_CTRLR_ATTACH || (n) == USB_EVENT_DEVICE_ATTACH || (n) == USB_EVENT_DRIVER_ATTACH)
+#define USB_EVENT_IS_DETACH(n) ((n) == USB_EVENT_CTRLR_DETACH || (n) == USB_EVENT_DEVICE_DETACH || (n) == USB_EVENT_DRIVER_DETACH)
+ struct timespec ue_time;
+ union {
+ struct {
+ int ue_bus;
+ } ue_ctrlr;
+ struct usb_device_info ue_device;
+ struct {
+ usb_event_cookie_t ue_cookie;
+ char ue_devname[16];
+ } ue_driver;
+ } u;
+};
+
+/* USB controller */
+#define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request)
+#define USB_SETDEBUG _IOW ('U', 2, int)
+#define USB_DISCOVER _IO ('U', 3)
+#define USB_DEVICEINFO _IOWR('U', 4, struct usb_device_info)
+#define USB_DEVICESTATS _IOR ('U', 5, struct usb_device_stats)
+
+/* Generic HID device */
+#define USB_GET_REPORT_DESC _IOR ('U', 21, struct usb_ctl_report_desc)
+#define USB_SET_IMMED _IOW ('U', 22, int)
+#define USB_GET_REPORT _IOWR('U', 23, struct usb_ctl_report)
+#define USB_SET_REPORT _IOW ('U', 24, struct usb_ctl_report)
+#define USB_GET_REPORT_ID _IOR ('U', 25, int)
+
+/* Generic USB device */
+#define USB_GET_CONFIG _IOR ('U', 100, int)
+#define USB_SET_CONFIG _IOW ('U', 101, int)
+#define USB_GET_ALTINTERFACE _IOWR('U', 102, struct usb_alt_interface)
+#define USB_SET_ALTINTERFACE _IOWR('U', 103, struct usb_alt_interface)
+#define USB_GET_NO_ALT _IOWR('U', 104, struct usb_alt_interface)
+#define USB_GET_DEVICE_DESC _IOR ('U', 105, usb_device_descriptor_t)
+#define USB_GET_CONFIG_DESC _IOWR('U', 106, struct usb_config_desc)
+#define USB_GET_INTERFACE_DESC _IOWR('U', 107, struct usb_interface_desc)
+#define USB_GET_ENDPOINT_DESC _IOWR('U', 108, struct usb_endpoint_desc)
+#define USB_GET_FULL_DESC _IOWR('U', 109, struct usb_full_desc)
+#define USB_GET_STRING_DESC _IOWR('U', 110, struct usb_string_desc)
+#define USB_DO_REQUEST _IOWR('U', 111, struct usb_ctl_request)
+#define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb_device_info)
+#define USB_SET_SHORT_XFER _IOW ('U', 113, int)
+#define USB_SET_TIMEOUT _IOW ('U', 114, int)
+#define USB_RESET_DEVICE _IO ('U', 115)
+
+/* Modem device */
+#define USB_GET_CM_OVER_DATA _IOR ('U', 130, int)
+#define USB_SET_CM_OVER_DATA _IOW ('U', 131, int)
+
+#endif /* _USB_H_ */
diff --git a/sys/legacy/dev/usb/usb_ethersubr.c b/sys/legacy/dev/usb/usb_ethersubr.c
new file mode 100644
index 0000000..d0a3b83
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_ethersubr.c
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Callbacks in the USB code operate at splusb() (actually splbio()
+ * in FreeBSD). However adding packets to the input queues has to be
+ * done at splimp(). It is conceivable that this arrangement could
+ * trigger a condition where the splimp() is ignored and the input
+ * queues could get trampled in spite of our best effors to prevent
+ * it. To work around this, we implement a special input queue for
+ * USB ethernet adapter drivers. Rather than passing the frames directly
+ * to ether_input(), we pass them here, then schedule a soft interrupt
+ * to hand them to ether_input() later, outside of the USB interrupt
+ * context.
+ *
+ * It's questional as to whether this code should be expanded to
+ * handle other kinds of devices, or handle USB transfer callbacks
+ * in general. Right now, I need USB network interfaces to work
+ * properly.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/taskqueue.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/netisr.h>
+#include <net/bpf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ethersubr.h>
+
+static struct ifqueue usbq_rx;
+static struct ifqueue usbq_tx;
+static int mtx_inited = 0;
+
+static void usbintr (void);
+
+static void
+usbintr(void)
+{
+ struct mbuf *m;
+ struct usb_qdat *q;
+ struct ifnet *ifp;
+
+ mtx_lock(&Giant);
+
+ /* Check the RX queue */
+ while(1) {
+ IF_DEQUEUE(&usbq_rx, m);
+ if (m == NULL)
+ break;
+ q = (struct usb_qdat *)m->m_pkthdr.rcvif;
+ ifp = q->ifp;
+ m->m_pkthdr.rcvif = ifp;
+ (*ifp->if_input)(ifp, m);
+
+ /* Re-arm the receiver */
+ (*q->if_rxstart)(ifp);
+ if (ifp->if_snd.ifq_head != NULL)
+ (*ifp->if_start)(ifp);
+ }
+
+ /* Check the TX queue */
+ while(1) {
+ IF_DEQUEUE(&usbq_tx, m);
+ if (m == NULL)
+ break;
+ ifp = m->m_pkthdr.rcvif;
+ m_freem(m);
+ if (ifp->if_snd.ifq_head != NULL)
+ (*ifp->if_start)(ifp);
+ }
+
+ mtx_unlock(&Giant);
+
+ return;
+}
+
+void
+usb_register_netisr(void)
+{
+ if (mtx_inited)
+ return;
+ netisr_register(NETISR_USB, (netisr_t *)usbintr, NULL,
+ NETISR_FORCEQUEUE);
+ mtx_init(&usbq_tx.ifq_mtx, "usbq_tx_mtx", NULL, MTX_DEF);
+ mtx_init(&usbq_rx.ifq_mtx, "usbq_rx_mtx", NULL, MTX_DEF);
+ mtx_inited++;
+ return;
+}
+
+/*
+ * Must be called at splusb() (actually splbio()). This should be
+ * the case when called from a transfer callback routine.
+ */
+void
+usb_ether_input(m)
+ struct mbuf *m;
+{
+ IF_ENQUEUE(&usbq_rx, m);
+ schednetisr(NETISR_USB);
+
+ return;
+}
+
+void
+usb_tx_done(m)
+ struct mbuf *m;
+{
+ IF_ENQUEUE(&usbq_tx, m);
+ schednetisr(NETISR_USB);
+
+ return;
+}
+
+struct mbuf *
+usb_ether_newbuf(void)
+{
+ struct mbuf *m_new;
+
+ m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m_new == NULL)
+ return (NULL);
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+
+ m_adj(m_new, ETHER_ALIGN);
+ return (m_new);
+}
+
+int
+usb_ether_rx_list_init(void *sc, struct ue_cdata *cd,
+ usbd_device_handle ue_udev)
+{
+ struct ue_chain *c;
+ int i;
+
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ c = &cd->ue_rx_chain[i];
+ c->ue_sc = sc;
+ c->ue_idx = i;
+ c->ue_mbuf = usb_ether_newbuf();
+ if (c->ue_mbuf == NULL)
+ return (ENOBUFS);
+ if (c->ue_xfer == NULL) {
+ c->ue_xfer = usbd_alloc_xfer(ue_udev);
+ if (c->ue_xfer == NULL)
+ return (ENOBUFS);
+ c->ue_buf = usbd_alloc_buffer(c->ue_xfer, UE_BUFSZ);
+ if (c->ue_buf == NULL)
+ return (ENOBUFS);
+ }
+ }
+
+ return (0);
+}
+
+int
+usb_ether_tx_list_init(void *sc, struct ue_cdata *cd,
+ usbd_device_handle ue_udev)
+{
+ struct ue_chain *c;
+ int i;
+
+ for (i = 0; i < UE_TX_LIST_CNT; i++) {
+ c = &cd->ue_tx_chain[i];
+ c->ue_sc = sc;
+ c->ue_idx = i;
+ c->ue_mbuf = NULL;
+ if (c->ue_xfer == NULL) {
+ c->ue_xfer = usbd_alloc_xfer(ue_udev);
+ if (c->ue_xfer == NULL)
+ return (ENOBUFS);
+ c->ue_buf = usbd_alloc_buffer(c->ue_xfer, UE_BUFSZ);
+ if (c->ue_buf == NULL)
+ return (ENOBUFS);
+ }
+ }
+
+ return (0);
+}
+
+void
+usb_ether_rx_list_free(struct ue_cdata *cd)
+{
+ int i;
+
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ if (cd->ue_rx_chain[i].ue_mbuf != NULL) {
+ m_freem(cd->ue_rx_chain[i].ue_mbuf);
+ cd->ue_rx_chain[i].ue_mbuf = NULL;
+ }
+ if (cd->ue_rx_chain[i].ue_xfer != NULL) {
+ usbd_free_xfer(cd->ue_rx_chain[i].ue_xfer);
+ cd->ue_rx_chain[i].ue_xfer = NULL;
+ }
+ }
+}
+
+void
+usb_ether_tx_list_free(struct ue_cdata *cd)
+{
+ int i;
+
+ for (i = 0; i < UE_RX_LIST_CNT; i++) {
+ if (cd->ue_tx_chain[i].ue_mbuf != NULL) {
+ m_freem(cd->ue_tx_chain[i].ue_mbuf);
+ cd->ue_tx_chain[i].ue_mbuf = NULL;
+ }
+ if (cd->ue_tx_chain[i].ue_xfer != NULL) {
+ usbd_free_xfer(cd->ue_tx_chain[i].ue_xfer);
+ cd->ue_tx_chain[i].ue_xfer = NULL;
+ }
+ }
+}
+
+void
+usb_ether_task_init(device_t dev, int flags, struct usb_taskqueue *tq)
+{
+
+ /* nothing for now. */
+}
+
+void
+usb_ether_task_enqueue(struct usb_taskqueue *tq, struct task *task)
+{
+
+ taskqueue_enqueue(taskqueue_thread, task);
+}
+
+void
+usb_ether_task_drain(struct usb_taskqueue *tq, struct task *task)
+{
+
+ taskqueue_drain(taskqueue_thread, task);
+}
+
+void
+usb_ether_task_destroy(struct usb_taskqueue *tq)
+{
+
+ /* nothing for now */
+
+}
diff --git a/sys/legacy/dev/usb/usb_ethersubr.h b/sys/legacy/dev/usb/usb_ethersubr.h
new file mode 100644
index 0000000..f3d2e34
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_ethersubr.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * 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 Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USB_ETHERSUBR_H_
+#define _USB_ETHERSUBR_H_
+
+#include <sys/bus.h>
+#include <sys/module.h>
+
+#include <dev/usb/usbdi.h>
+
+#define UE_TX_LIST_CNT 1
+#define UE_RX_LIST_CNT 1
+#define UE_BUFSZ 1536
+
+struct usb_qdat {
+ struct ifnet *ifp;
+ void (*if_rxstart) (struct ifnet *);
+};
+
+struct ue_chain {
+ void *ue_sc;
+ usbd_xfer_handle ue_xfer;
+ char *ue_buf;
+ struct mbuf *ue_mbuf;
+ int ue_idx;
+ usbd_status ue_status;
+};
+
+struct ue_cdata {
+ struct ue_chain ue_tx_chain[UE_TX_LIST_CNT];
+ struct ue_chain ue_rx_chain[UE_RX_LIST_CNT];
+ void *ue_ibuf;
+ int ue_tx_prod;
+ int ue_tx_cons;
+ int ue_tx_cnt;
+ int ue_rx_prod;
+};
+
+void usb_register_netisr (void);
+void usb_ether_input (struct mbuf *);
+void usb_tx_done (struct mbuf *);
+struct mbuf *usb_ether_newbuf (void);
+int usb_ether_rx_list_init (void *, struct ue_cdata *,
+ usbd_device_handle);
+int usb_ether_tx_list_init (void *, struct ue_cdata *,
+ usbd_device_handle);
+void usb_ether_rx_list_free (struct ue_cdata *);
+void usb_ether_tx_list_free (struct ue_cdata *);
+
+struct usb_taskqueue {
+ int dummy;
+};
+
+void usb_ether_task_init(device_t, int, struct usb_taskqueue *);
+void usb_ether_task_enqueue(struct usb_taskqueue *, struct task *);
+void usb_ether_task_drain(struct usb_taskqueue *, struct task *);
+void usb_ether_task_destroy(struct usb_taskqueue *);
+
+#endif /* _USB_ETHERSUBR_H_ */
diff --git a/sys/legacy/dev/usb/usb_if.m b/sys/legacy/dev/usb/usb_if.m
new file mode 100644
index 0000000..b04c8a4
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_if.m
@@ -0,0 +1,42 @@
+#-
+# Copyright (c) 1992-1998 Nick Hibma <n_hibma@freebsd.org>
+# All rights reserved.
+#
+# 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,
+# without modification, immediately at the beginning of the file.
+# 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. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+#
+# $FreeBSD$
+#
+
+# USB interface description
+#
+
+#include <sys/bus.h>
+
+INTERFACE usb;
+
+# The device should start probing for new drivers again
+#
+METHOD int reconfigure {
+ device_t dev;
+};
diff --git a/sys/legacy/dev/usb/usb_mem.c b/sys/legacy/dev/usb/usb_mem.c
new file mode 100644
index 0000000..bbb0bf6
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_mem.c
@@ -0,0 +1,297 @@
+/* $NetBSD: usb_mem.c,v 1.26 2003/02/01 06:23:40 thorpej Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+/*
+ * USB DMA memory allocation.
+ * We need to allocate a lot of small (many 8 byte, some larger)
+ * memory blocks that can be used for DMA. Using the bus_dma
+ * routines directly would incur large overheads in space and time.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+
+#ifdef DIAGNOSTIC
+#include <sys/proc.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h> /* just for usb_dma_t */
+#include <dev/usb/usb_mem.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define USB_MEM_SMALL 64
+#define USB_MEM_CHUNKS (PAGE_SIZE / USB_MEM_SMALL)
+#define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS)
+
+/* This struct is overlayed on free fragments. */
+struct usb_frag_dma {
+ usb_dma_block_t *block;
+ u_int offs;
+ LIST_ENTRY(usb_frag_dma) next;
+};
+
+static bus_dmamap_callback_t usbmem_callback;
+static usbd_status usb_block_allocmem(bus_dma_tag_t, size_t, size_t,
+ usb_dma_block_t **);
+static void usb_block_freemem(usb_dma_block_t *);
+
+static LIST_HEAD(, usb_dma_block) usb_blk_freelist =
+ LIST_HEAD_INITIALIZER(usb_blk_freelist);
+static int usb_blk_nfree = 0;
+/* XXX should have different free list for different tags (for speed) */
+static LIST_HEAD(, usb_frag_dma) usb_frag_freelist =
+ LIST_HEAD_INITIALIZER(usb_frag_freelist);
+
+static void
+usbmem_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ int i;
+ usb_dma_block_t *p = arg;
+
+ if (error == EFBIG) {
+ printf("usb: mapping to large\n");
+ return;
+ }
+
+ p->nsegs = nseg;
+ for (i = 0; i < nseg && i < sizeof p->segs / sizeof *p->segs; i++)
+ p->segs[i] = segs[i];
+}
+
+static usbd_status
+usb_block_allocmem(bus_dma_tag_t tag, size_t size, size_t align,
+ usb_dma_block_t **dmap)
+{
+ usb_dma_block_t *p;
+ int s;
+
+ DPRINTFN(5, ("usb_block_allocmem: size=%lu align=%lu\n",
+ (u_long)size, (u_long)align));
+
+#ifdef DIAGNOSTIC
+ if (!curproc) {
+ printf("usb_block_allocmem: in interrupt context, size=%lu\n",
+ (unsigned long) size);
+ }
+#endif
+
+ s = splusb();
+ /* First check the free list. */
+ for (p = LIST_FIRST(&usb_blk_freelist); p; p = LIST_NEXT(p, next)) {
+ if (p->tag == tag && p->size >= size && p->size < size * 2 &&
+ p->align >= align) {
+ LIST_REMOVE(p, next);
+ usb_blk_nfree--;
+ splx(s);
+ *dmap = p;
+ DPRINTFN(6,("usb_block_allocmem: free list size=%lu\n",
+ (u_long)p->size));
+ return (USBD_NORMAL_COMPLETION);
+ }
+ }
+ splx(s);
+
+#ifdef DIAGNOSTIC
+ if (!curproc) {
+ printf("usb_block_allocmem: in interrupt context, failed\n");
+ return (USBD_NOMEM);
+ }
+#endif
+
+ DPRINTFN(6, ("usb_block_allocmem: no free\n"));
+ p = malloc(sizeof *p, M_USB, M_NOWAIT);
+ if (p == NULL)
+ return (USBD_NOMEM);
+
+ if (bus_dma_tag_create(tag, align, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ size, sizeof(p->segs) / sizeof(p->segs[0]), size,
+ 0, NULL, NULL, &p->tag) == ENOMEM)
+ {
+ goto free;
+ }
+
+ p->size = size;
+ p->align = align;
+ if (bus_dmamem_alloc(p->tag, &p->kaddr,
+ BUS_DMA_NOWAIT|BUS_DMA_COHERENT, &p->map))
+ goto tagfree;
+
+ if (bus_dmamap_load(p->tag, p->map, p->kaddr, p->size,
+ usbmem_callback, p, 0))
+ goto memfree;
+
+ /* XXX - override the tag, ok since we never free it */
+ p->tag = tag;
+ *dmap = p;
+ return (USBD_NORMAL_COMPLETION);
+
+ /*
+ * XXX - do we need to _unload? is the order of _free and _destroy
+ * correct?
+ */
+memfree:
+ bus_dmamem_free(p->tag, p->kaddr, p->map);
+tagfree:
+ bus_dma_tag_destroy(p->tag);
+free:
+ free(p, M_USB);
+ return (USBD_NOMEM);
+}
+
+/*
+ * Do not free the memory unconditionally since we might be called
+ * from an interrupt context and that is BAD.
+ * XXX when should we really free?
+ */
+static void
+usb_block_freemem(usb_dma_block_t *p)
+{
+ int s;
+
+ DPRINTFN(6, ("usb_block_freemem: size=%lu\n", (u_long)p->size));
+ s = splusb();
+ LIST_INSERT_HEAD(&usb_blk_freelist, p, next);
+ usb_blk_nfree++;
+ splx(s);
+}
+
+usbd_status
+usb_allocmem(usbd_bus_handle bus, size_t size, size_t align, usb_dma_t *p)
+{
+ bus_dma_tag_t tag = bus->parent_dmatag;
+ usbd_status err;
+ struct usb_frag_dma *f;
+ usb_dma_block_t *b;
+ int i;
+ int s;
+
+ /* compat w/ Net/OpenBSD */
+ if (align == 0)
+ align = 1;
+
+ /* If the request is large then just use a full block. */
+ if (size > USB_MEM_SMALL || align > USB_MEM_SMALL) {
+ DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size));
+ size = (size + USB_MEM_BLOCK - 1) & ~(USB_MEM_BLOCK - 1);
+ err = usb_block_allocmem(tag, size, align, &p->block);
+ if (!err) {
+ p->block->fullblock = 1;
+ p->offs = 0;
+ p->len = size;
+ }
+ return (err);
+ }
+
+ s = splusb();
+ /* Check for free fragments. */
+ for (f = LIST_FIRST(&usb_frag_freelist); f; f = LIST_NEXT(f, next))
+ if (f->block->tag == tag)
+ break;
+ if (f == NULL) {
+ DPRINTFN(1, ("usb_allocmem: adding fragments\n"));
+ err = usb_block_allocmem(tag, USB_MEM_BLOCK, USB_MEM_SMALL,&b);
+ if (err) {
+ splx(s);
+ return (err);
+ }
+ b->fullblock = 0;
+ /* XXX - override the tag, ok since we never free it */
+ b->tag = tag;
+ KASSERT(sizeof *f <= USB_MEM_SMALL, ("USB_MEM_SMALL(%d) is too small for struct usb_frag_dma(%zd)\n",
+ USB_MEM_SMALL, sizeof *f));
+ for (i = 0; i < USB_MEM_BLOCK; i += USB_MEM_SMALL) {
+ f = (struct usb_frag_dma *)((char *)b->kaddr + i);
+ f->block = b;
+ f->offs = i;
+ LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
+ }
+ f = LIST_FIRST(&usb_frag_freelist);
+ }
+ p->block = f->block;
+ p->offs = f->offs;
+ p->len = USB_MEM_SMALL;
+ LIST_REMOVE(f, next);
+ splx(s);
+ DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f, (int)size));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usb_freemem(usbd_bus_handle bus, usb_dma_t *p)
+{
+ struct usb_frag_dma *f;
+ int s;
+
+ if (p->block->fullblock) {
+ DPRINTFN(1, ("usb_freemem: large free\n"));
+ usb_block_freemem(p->block);
+ return;
+ }
+ f = KERNADDR(p, 0);
+ f->block = p->block;
+ f->offs = p->offs;
+ s = splusb();
+ LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
+ splx(s);
+ DPRINTFN(5, ("usb_freemem: frag=%p\n", f));
+}
diff --git a/sys/legacy/dev/usb/usb_mem.h b/sys/legacy/dev/usb/usb_mem.h
new file mode 100644
index 0000000..b8f0a14
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_mem.h
@@ -0,0 +1,58 @@
+/* $NetBSD: usb_mem.h,v 1.18 2002/05/28 17:45:17 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+typedef struct usb_dma_block {
+ bus_dma_tag_t tag;
+ bus_dmamap_t map;
+ void *kaddr;
+ bus_dma_segment_t segs[1];
+ int nsegs;
+ size_t size;
+ size_t align;
+ int fullblock;
+ LIST_ENTRY(usb_dma_block) next;
+} usb_dma_block_t;
+
+#define DMAADDR(dma, o) ((dma)->block->segs[0].ds_addr + (dma)->offs + (o))
+#define KERNADDR(dma, o) \
+ ((void *)((char *)((dma)->block->kaddr) + (dma)->offs + (o)))
+
+usbd_status usb_allocmem(usbd_bus_handle,size_t,size_t, usb_dma_t *);
+void usb_freemem(usbd_bus_handle, usb_dma_t *);
diff --git a/sys/legacy/dev/usb/usb_port.h b/sys/legacy/dev/usb/usb_port.h
new file mode 100644
index 0000000..fd92d7b
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_port.h
@@ -0,0 +1,200 @@
+/* $OpenBSD: usb_port.h,v 1.18 2000/09/06 22:42:10 rahnds Exp $ */
+/* $NetBSD: usb_port.h,v 1.54 2002/03/28 21:49:19 ichiro Exp $ */
+/* $FreeBSD$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: usb_port.h,v 1.57 2002/09/27 20:42:01 thorpej Exp $
+ * $NetBSD: usb_port.h,v 1.58 2002/10/01 01:25:26 thorpej 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.
+ */
+
+#ifndef _USB_PORT_H
+#define _USB_PORT_H
+
+/*
+ * Macro's to cope with the differences between operating systems.
+ */
+
+/*
+ * FreeBSD
+ */
+
+/* We don't use the soft interrupt code in FreeBSD. */
+#if 0
+#define USB_USE_SOFTINTR
+#endif
+
+#define Static static
+
+#define device_ptr_t device_t
+#define USBBASEDEVICE device_t
+#define USBDEV(bdev) (bdev)
+#define USBDEVNAME(bdev) device_get_nameunit(bdev)
+#define USBDEVPTRNAME(bdev) device_get_nameunit(bdev)
+#define USBDEVUNIT(bdev) device_get_unit(bdev)
+#define USBGETSOFTC(bdev) (device_get_softc(bdev))
+
+#define DECLARE_USB_DMA_T \
+ struct usb_dma_block; \
+ typedef struct { \
+ struct usb_dma_block *block; \
+ u_int offs; \
+ u_int len; \
+ } usb_dma_t
+
+typedef struct thread *usb_proc_ptr;
+
+#define uio_procp uio_td
+
+#define usb_kthread_create1(f, s, p, a0, a1) \
+ kproc_create((f), (s), (p), RFHIGHPID, 0, (a0), (a1))
+#define usb_kthread_create2(f, s, p, a0) \
+ kproc_create((f), (s), (p), RFHIGHPID, 0, (a0))
+#define usb_kthread_create kproc_create
+
+#define config_pending_incr()
+#define config_pending_decr()
+
+typedef struct callout usb_callout_t;
+#define usb_callout_init(h) callout_init(&(h), 0)
+#define usb_callout(h, t, f, d) callout_reset(&(h), (t), (f), (d))
+#define usb_uncallout(h, f, d) callout_stop(&(h))
+#define usb_uncallout_drain(h, f, d) callout_drain(&(h))
+
+#define clalloc(p, s, x) (clist_alloc_cblocks((p), (s), (s)), 0)
+#define clfree(p) clist_free_cblocks((p))
+
+#define config_detach(dev, flag) \
+ do { \
+ device_detach(dev); \
+ free(device_get_ivars(dev), M_USB); \
+ device_delete_child(device_get_parent(dev), dev); \
+ } while (0);
+
+typedef struct malloc_type *usb_malloc_type;
+
+#define USB_DECLARE_DRIVER_INIT(dname, init...) \
+static device_probe_t __CONCAT(dname,_match); \
+static device_attach_t __CONCAT(dname,_attach); \
+static device_detach_t __CONCAT(dname,_detach); \
+\
+static devclass_t __CONCAT(dname,_devclass); \
+\
+static device_method_t __CONCAT(dname,_methods)[] = { \
+ DEVMETHOD(device_probe, __CONCAT(dname,_match)), \
+ DEVMETHOD(device_attach, __CONCAT(dname,_attach)), \
+ DEVMETHOD(device_detach, __CONCAT(dname,_detach)), \
+ init, \
+ {0,0} \
+}; \
+\
+static driver_t __CONCAT(dname,_driver) = { \
+ #dname, \
+ __CONCAT(dname,_methods), \
+ sizeof(struct __CONCAT(dname,_softc)) \
+}; \
+MODULE_DEPEND(dname, usb, 1, 1, 1)
+
+
+#define METHODS_NONE {0,0}
+#define USB_DECLARE_DRIVER(dname) USB_DECLARE_DRIVER_INIT(dname, METHODS_NONE)
+
+#define USB_MATCH(dname) \
+static int \
+__CONCAT(dname,_match)(device_t self)
+
+#define USB_MATCH_START(dname, uaa) \
+ struct usb_attach_arg *uaa = device_get_ivars(self)
+
+#define USB_MATCH_SETUP \
+ sc->sc_dev = self
+
+#define USB_ATTACH(dname) \
+static int \
+__CONCAT(dname,_attach)(device_t self)
+
+#define USB_ATTACH_START(dname, sc, uaa) \
+ struct __CONCAT(dname,_softc) *sc = device_get_softc(self); \
+ struct usb_attach_arg *uaa = device_get_ivars(self)
+
+/* Returns from attach */
+#define USB_ATTACH_ERROR_RETURN return ENXIO
+#define USB_ATTACH_SUCCESS_RETURN return 0
+
+#define USB_ATTACH_SETUP \
+ sc->sc_dev = self; \
+
+#define USB_DETACH(dname) \
+static int \
+__CONCAT(dname,_detach)(device_t self)
+
+#define USB_DETACH_START(dname, sc) \
+ struct __CONCAT(dname,_softc) *sc = device_get_softc(self)
+
+#define USB_GET_SC_OPEN(dname, unit, sc) \
+ sc = devclass_get_softc(__CONCAT(dname,_devclass), unit); \
+ if (sc == NULL) \
+ return (ENXIO)
+
+#define USB_GET_SC(dname, unit, sc) \
+ sc = devclass_get_softc(__CONCAT(dname,_devclass), unit)
+
+#define USB_DO_ATTACH(dev, bdev, parent, args, print, sub) \
+ (device_probe_and_attach((bdev)) == 0 ? (bdev) : 0)
+
+/* conversion from one type of queue to the other */
+#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD
+#define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD
+#define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL
+#define SIMPLEQ_NEXT STAILQ_NEXT
+#define SIMPLEQ_FIRST STAILQ_FIRST
+#define SIMPLEQ_HEAD STAILQ_HEAD
+#define SIMPLEQ_EMPTY STAILQ_EMPTY
+#define SIMPLEQ_FOREACH STAILQ_FOREACH
+#define SIMPLEQ_INIT STAILQ_INIT
+#define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER
+#define SIMPLEQ_ENTRY STAILQ_ENTRY
+
+#include <sys/syslog.h>
+/*
+#define logprintf(args...) log(LOG_DEBUG, args)
+*/
+#define logprintf printf
+
+#endif /* _USB_PORT_H */
diff --git a/sys/legacy/dev/usb/usb_quirks.c b/sys/legacy/dev/usb/usb_quirks.c
new file mode 100644
index 0000000..2771bb7
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_quirks.c
@@ -0,0 +1,153 @@
+/* $NetBSD: usb_quirks.c,v 1.50 2004/06/23 02:30:52 mycroft 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <dev/usb/usb.h>
+
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+
+#ifdef USB_DEBUG
+extern int usbdebug;
+#endif
+
+#define ANY 0xffff
+
+static const struct usbd_quirk_entry {
+ u_int16_t idVendor;
+ u_int16_t idProduct;
+ u_int16_t bcdDevice;
+ struct usbd_quirks quirks;
+} usb_quirks[] = {
+ { USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4,
+ 0x094, { UQ_SWAP_UNICODE}},
+ { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_BAD_ADC }},
+ { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, { UQ_AU_NO_XU }},
+ { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 0x103, { UQ_BAD_ADC }},
+ { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 0x000, { UQ_BAD_AUDIO }},
+ { USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0x110, { UQ_SPUR_BUT_UP }},
+ { USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, { UQ_SPUR_BUT_UP }},
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, 0x102, { UQ_BUS_POWERED }},
+ { USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0x102, { UQ_BUS_POWERED }},
+ { USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, 0x110, { UQ_POWER_CLAIM }},
+ { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 0x009, { UQ_AU_NO_FRAC }},
+ { USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE,
+ 0x100, { UQ_AU_INP_ASYNC }},
+ { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_UN53B, ANY, { UQ_NO_STRINGS }},
+ /* XXX These should have a revision number, but I don't know what they are. */
+ { USB_VENDOR_HP, USB_PRODUCT_HP_895C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_880C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_815C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_810C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_830C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_HP, USB_PRODUCT_HP_1220C, ANY, { UQ_BROKEN_BIDIR }},
+ { USB_VENDOR_XEROX, USB_PRODUCT_XEROX_WCM15, ANY, { UQ_BROKEN_BIDIR }},
+ /* MS keyboards do weird things */
+ { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK,
+ ANY, { UQ_MS_BAD_CLASS | UQ_MS_LEADING_BYTE }},
+ { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK2,
+ ANY, { UQ_MS_BAD_CLASS | UQ_MS_LEADING_BYTE }},
+ { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLINTELLIMOUSE,
+ ANY, { UQ_MS_LEADING_BYTE }},
+ { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_COMFORT3000,
+ ANY, { UQ_MS_BAD_CLASS | UQ_MS_LEADING_BYTE }},
+ { USB_VENDOR_SONY, USB_PRODUCT_SONY_RF_RECEIVER,
+ ANY,{ UQ_MS_BAD_CLASS }},
+
+ /* Devices which should be ignored by uhid */
+ { USB_VENDOR_APC, USB_PRODUCT_APC_UPS,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_ASUS, USB_PRODUCT_ASUS_LCM,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C550AVR,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_1500CAVRLCD,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD2X20,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
+ ANY, { UQ_HID_IGNORE }},
+ { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
+ ANY, { UQ_HID_IGNORE }},
+
+ /* Devices which should be ignored by both ukbd and uhid */
+ { USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_WISPY1A,
+ ANY, { UQ_KBD_IGNORE }},
+ { USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY1B,
+ ANY, { UQ_KBD_IGNORE }},
+ { USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY24X,
+ ANY, { UQ_KBD_IGNORE }},
+ { 0, 0, 0, { 0 } }
+};
+
+const struct usbd_quirks usbd_no_quirk = { 0 };
+
+const struct usbd_quirks *
+usbd_find_quirk(usb_device_descriptor_t *d)
+{
+ const struct usbd_quirk_entry *t;
+ u_int16_t vendor = UGETW(d->idVendor);
+ u_int16_t product = UGETW(d->idProduct);
+ u_int16_t revision = UGETW(d->bcdDevice);
+
+ for (t = usb_quirks; t->idVendor != 0; t++) {
+ if (t->idVendor == vendor &&
+ t->idProduct == product &&
+ (t->bcdDevice == ANY || t->bcdDevice == revision))
+ break;
+ }
+#ifdef USB_DEBUG
+ if (usbdebug && t->quirks.uq_flags)
+ printf("usbd_find_quirk 0x%04x/0x%04x/%x: %d\n",
+ UGETW(d->idVendor), UGETW(d->idProduct),
+ UGETW(d->bcdDevice), t->quirks.uq_flags);
+#endif
+ return (&t->quirks);
+}
diff --git a/sys/legacy/dev/usb/usb_quirks.h b/sys/legacy/dev/usb/usb_quirks.h
new file mode 100644
index 0000000..16e2f3f
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_quirks.h
@@ -0,0 +1,64 @@
+/* $NetBSD: usb_quirks.h,v 1.20 2001/04/15 09:38:01 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+struct usbd_quirks {
+ u_int32_t uq_flags; /* Device problems: */
+#define UQ_SWAP_UNICODE 0x00000002 /* has some Unicode strings swapped. */
+#define UQ_MS_REVZ 0x00000004 /* mouse has Z-axis reversed */
+#define UQ_NO_STRINGS 0x00000008 /* string descriptors are broken. */
+#define UQ_BAD_ADC 0x00000010 /* bad audio spec version number. */
+#define UQ_BUS_POWERED 0x00000020 /* device is bus powered, despite claim */
+#define UQ_BAD_AUDIO 0x00000040 /* device claims audio class, but isn't */
+#define UQ_SPUR_BUT_UP 0x00000080 /* spurious mouse button up events */
+#define UQ_AU_NO_XU 0x00000100 /* audio device has broken extension unit */
+#define UQ_POWER_CLAIM 0x00000200 /* hub lies about power status */
+#define UQ_AU_NO_FRAC 0x00000400 /* don't adjust for fractional samples */
+#define UQ_AU_INP_ASYNC 0x00000800 /* input is async despite claim of adaptive */
+#define UQ_BROKEN_BIDIR 0x00002000 /* printer has broken bidir mode */
+#define UQ_OPEN_CLEARSTALL 0x04000 /* device needs clear endpoint stall */
+#define UQ_HID_IGNORE 0x00008000 /* device should be ignored by hid class */
+#define UQ_KBD_IGNORE 0x00018000 /* device should be ignored by both kbd and hid class */
+#define UQ_MS_BAD_CLASS 0x00020000 /* doesn't identify properly */
+#define UQ_MS_LEADING_BYTE 0x40000 /* mouse sends an unknown leading byte. */
+};
+
+extern const struct usbd_quirks usbd_no_quirk;
+
+const struct usbd_quirks *usbd_find_quirk(usb_device_descriptor_t *);
diff --git a/sys/legacy/dev/usb/usb_subr.c b/sys/legacy/dev/usb/usb_subr.c
new file mode 100644
index 0000000..29d044c
--- /dev/null
+++ b/sys/legacy/dev/usb/usb_subr.c
@@ -0,0 +1,1388 @@
+/* $NetBSD: usb_subr.c,v 1.99 2002/07/11 21:14:34 augustss Exp $ */
+
+/* Also already have from NetBSD:
+ * $NetBSD: usb_subr.c,v 1.102 2003/01/01 16:21:50 augustss Exp $
+ * $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $
+ * $NetBSD: usb_subr.c,v 1.111 2004/03/15 10:35:04 augustss Exp $
+ * $NetBSD: usb_subr.c,v 1.114 2004/06/23 02:30:52 mycroft Exp $
+ * $NetBSD: usb_subr.c,v 1.115 2004/06/23 05:23:19 mycroft Exp $
+ * $NetBSD: usb_subr.c,v 1.116 2004/06/23 06:27:54 mycroft Exp $
+ * $NetBSD: usb_subr.c,v 1.119 2004/10/23 13:26:33 augustss Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * 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.
+ */
+
+#include "opt_usb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+
+#define delay(d) DELAY(d)
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+static usbd_status usbd_set_config(usbd_device_handle, int);
+static void usbd_devinfo_vp(usbd_device_handle, char *, char *, int);
+static int usbd_getnewaddr(usbd_bus_handle bus);
+static void usbd_free_iface_data(usbd_device_handle dev, int ifcno);
+static void usbd_kill_pipe(usbd_pipe_handle);
+static usbd_status usbd_probe_and_attach(device_t parent,
+ usbd_device_handle dev, int port, int addr);
+
+static u_int32_t usb_cookie_no = 0;
+
+#ifdef USBVERBOSE
+typedef u_int16_t usb_vendor_id_t;
+typedef u_int16_t usb_product_id_t;
+
+/*
+ * Descriptions of of known vendors and devices ("products").
+ */
+struct usb_knowndev {
+ usb_vendor_id_t vendor;
+ usb_product_id_t product;
+ int flags;
+ char *vendorname, *productname;
+};
+#define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */
+
+#include "usbdevs_data.h"
+#endif /* USBVERBOSE */
+
+static const char * const usbd_error_strs[] = {
+ "NORMAL_COMPLETION",
+ "IN_PROGRESS",
+ "PENDING_REQUESTS",
+ "NOT_STARTED",
+ "INVAL",
+ "NOMEM",
+ "CANCELLED",
+ "BAD_ADDRESS",
+ "IN_USE",
+ "NO_ADDR",
+ "SET_ADDR_FAILED",
+ "NO_POWER",
+ "TOO_DEEP",
+ "IOERROR",
+ "NOT_CONFIGURED",
+ "TIMEOUT",
+ "SHORT_XFER",
+ "STALLED",
+ "INTERRUPTED",
+ "XXX",
+};
+
+const char *
+usbd_errstr(usbd_status err)
+{
+ static char buffer[5];
+
+ if (err < USBD_ERROR_MAX) {
+ return usbd_error_strs[err];
+ } else {
+ snprintf(buffer, sizeof buffer, "%d", err);
+ return buffer;
+ }
+}
+
+usbd_status
+usbd_get_string_desc(usbd_device_handle dev, int sindex, int langid,
+ usb_string_descriptor_t *sdesc, int *sizep)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ int actlen;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_STRING, sindex);
+ USETW(req.wIndex, langid);
+ USETW(req.wLength, 2); /* only size byte first */
+ err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK,
+ &actlen, USBD_DEFAULT_TIMEOUT);
+ if (err)
+ return (err);
+
+ if (actlen < 2)
+ return (USBD_SHORT_XFER);
+
+ USETW(req.wLength, sdesc->bLength); /* the whole string */
+ err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK,
+ &actlen, USBD_DEFAULT_TIMEOUT);
+ if (err)
+ return (err);
+
+ if (actlen != sdesc->bLength) {
+ DPRINTFN(-1, ("usbd_get_string_desc: expected %d, got %d\n",
+ sdesc->bLength, actlen));
+ }
+
+ *sizep = actlen;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static void
+usbd_trim_spaces(char *p)
+{
+ char *q, *e;
+
+ if (p == NULL)
+ return;
+ q = e = p;
+ while (*q == ' ') /* skip leading spaces */
+ q++;
+ while ((*p = *q++)) /* copy string */
+ if (*p++ != ' ') /* remember last non-space */
+ e = p;
+ *e = 0; /* kill trailing spaces */
+}
+
+static void
+usbd_devinfo_vp(usbd_device_handle dev, char *v, char *p, int usedev)
+{
+ usb_device_descriptor_t *udd = &dev->ddesc;
+ char *vendor = 0, *product = 0;
+#ifdef USBVERBOSE
+ const struct usb_knowndev *kdp;
+#endif
+
+ if (dev == NULL) {
+ v[0] = p[0] = '\0';
+ return;
+ }
+
+ if (usedev) {
+ if (usbd_get_string(dev, udd->iManufacturer, v,
+ USB_MAX_STRING_LEN))
+ vendor = NULL;
+ else
+ vendor = v;
+ usbd_trim_spaces(vendor);
+ if (usbd_get_string(dev, udd->iProduct, p,
+ USB_MAX_STRING_LEN))
+ product = NULL;
+ else
+ product = p;
+ usbd_trim_spaces(product);
+ if (vendor && !*vendor)
+ vendor = NULL;
+ if (product && !*product)
+ product = NULL;
+ } else {
+ vendor = NULL;
+ product = NULL;
+ }
+#ifdef USBVERBOSE
+ if (vendor == NULL || product == NULL) {
+ for(kdp = usb_knowndevs;
+ kdp->vendorname != NULL;
+ kdp++) {
+ if (kdp->vendor == UGETW(udd->idVendor) &&
+ (kdp->product == UGETW(udd->idProduct) ||
+ (kdp->flags & USB_KNOWNDEV_NOPROD) != 0))
+ break;
+ }
+ if (kdp->vendorname != NULL) {
+ if (vendor == NULL)
+ vendor = kdp->vendorname;
+ if (product == NULL)
+ product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ?
+ kdp->productname : NULL;
+ }
+ }
+#endif
+ if (vendor != NULL && *vendor)
+ strcpy(v, vendor);
+ else
+ sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor));
+ if (product != NULL && *product)
+ strcpy(p, product);
+ else
+ sprintf(p, "product 0x%04x", UGETW(udd->idProduct));
+}
+
+int
+usbd_printBCD(char *cp, int bcd)
+{
+ return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff));
+}
+
+void
+usbd_devinfo(usbd_device_handle dev, int showclass, char *cp)
+{
+ usb_device_descriptor_t *udd = &dev->ddesc;
+ usbd_interface_handle iface;
+ char vendor[USB_MAX_STRING_LEN];
+ char product[USB_MAX_STRING_LEN];
+ int bcdDevice, bcdUSB;
+ usb_interface_descriptor_t *id;
+
+ usbd_devinfo_vp(dev, vendor, product, 1);
+ cp += sprintf(cp, "%s %s", vendor, product);
+ if (showclass & USBD_SHOW_DEVICE_CLASS)
+ cp += sprintf(cp, ", class %d/%d",
+ udd->bDeviceClass, udd->bDeviceSubClass);
+ bcdUSB = UGETW(udd->bcdUSB);
+ bcdDevice = UGETW(udd->bcdDevice);
+ cp += sprintf(cp, ", rev ");
+ cp += usbd_printBCD(cp, bcdUSB);
+ *cp++ = '/';
+ cp += usbd_printBCD(cp, bcdDevice);
+ cp += sprintf(cp, ", addr %d", dev->address);
+ if (showclass & USBD_SHOW_INTERFACE_CLASS)
+ {
+ /* fetch the interface handle for the first interface */
+ (void)usbd_device2interface_handle(dev, 0, &iface);
+ id = usbd_get_interface_descriptor(iface);
+ cp += sprintf(cp, ", iclass %d/%d",
+ id->bInterfaceClass, id->bInterfaceSubClass);
+ }
+ *cp = 0;
+}
+
+/* Delay for a certain number of ms */
+void
+usb_delay_ms(usbd_bus_handle bus, u_int ms)
+{
+ /* Wait at least two clock ticks so we know the time has passed. */
+ if (bus->use_polling || cold)
+ delay((ms+1) * 1000);
+ else
+ pause("usbdly", (ms*hz+999)/1000 + 1);
+}
+
+/* Delay given a device handle. */
+void
+usbd_delay_ms(usbd_device_handle dev, u_int ms)
+{
+ usb_delay_ms(dev->bus, ms);
+}
+
+usbd_status
+usbd_reset_port(usbd_device_handle dev, int port, usb_port_status_t *ps)
+{
+ usbd_status err;
+ int n;
+
+ err = usbd_set_port_feature(dev, port, UHF_PORT_RESET);
+ DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%s\n",
+ port, usbd_errstr(err)));
+ if (err)
+ return (err);
+ n = 10;
+ do {
+ /* Wait for device to recover from reset. */
+ usbd_delay_ms(dev, USB_PORT_RESET_DELAY);
+ err = usbd_get_port_status(dev, port, ps);
+ if (err) {
+ DPRINTF(("usbd_reset_port: get status failed %d\n",
+ err));
+ return (err);
+ }
+ /* If the device disappeared, just give up. */
+ if (!(UGETW(ps->wPortStatus) & UPS_CURRENT_CONNECT_STATUS))
+ return (USBD_NORMAL_COMPLETION);
+ } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0);
+ if (n == 0)
+ return (USBD_TIMEOUT);
+ err = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET);
+#ifdef USB_DEBUG
+ if (err)
+ DPRINTF(("usbd_reset_port: clear port feature failed %d\n",
+ err));
+#endif
+
+ /* Wait for the device to recover from reset. */
+ usbd_delay_ms(dev, USB_PORT_RESET_RECOVERY);
+ return (err);
+}
+
+usb_interface_descriptor_t *
+usbd_find_idesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx)
+{
+ char *p = (char *)cd;
+ char *end = p + UGETW(cd->wTotalLength);
+ usb_interface_descriptor_t *d;
+ int curidx, lastidx, curaidx = 0;
+
+ for (curidx = lastidx = -1; p < end; ) {
+ d = (usb_interface_descriptor_t *)p;
+ DPRINTFN(4,("usbd_find_idesc: idx=%d(%d) altidx=%d(%d) len=%d "
+ "type=%d\n",
+ ifaceidx, curidx, altidx, curaidx,
+ d->bLength, d->bDescriptorType));
+ if (d->bLength == 0) /* bad descriptor */
+ break;
+ p += d->bLength;
+ if (p <= end && d->bDescriptorType == UDESC_INTERFACE) {
+ if (d->bInterfaceNumber != lastidx) {
+ lastidx = d->bInterfaceNumber;
+ curidx++;
+ curaidx = 0;
+ } else
+ curaidx++;
+ if (ifaceidx == curidx && altidx == curaidx)
+ return (d);
+ }
+ }
+ return (NULL);
+}
+
+usb_endpoint_descriptor_t *
+usbd_find_edesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx,
+ int endptidx)
+{
+ char *p = (char *)cd;
+ char *end = p + UGETW(cd->wTotalLength);
+ usb_interface_descriptor_t *d;
+ usb_endpoint_descriptor_t *e;
+ int curidx;
+
+ d = usbd_find_idesc(cd, ifaceidx, altidx);
+ if (d == NULL)
+ return (NULL);
+ if (endptidx >= d->bNumEndpoints) /* quick exit */
+ return (NULL);
+
+ curidx = -1;
+ for (p = (char *)d + d->bLength; p < end; ) {
+ e = (usb_endpoint_descriptor_t *)p;
+ if (e->bLength == 0) /* bad descriptor */
+ break;
+ p += e->bLength;
+ if (p <= end && e->bDescriptorType == UDESC_INTERFACE)
+ return (NULL);
+ if (p <= end && e->bDescriptorType == UDESC_ENDPOINT) {
+ curidx++;
+ if (curidx == endptidx)
+ return (e);
+ }
+ }
+ return (NULL);
+}
+
+usbd_status
+usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx)
+{
+ usbd_interface_handle ifc = &dev->ifaces[ifaceidx];
+ usb_interface_descriptor_t *idesc;
+ char *p, *end;
+ int endpt, nendpt;
+
+ DPRINTFN(4,("usbd_fill_iface_data: ifaceidx=%d altidx=%d\n",
+ ifaceidx, altidx));
+ idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx);
+ if (idesc == NULL)
+ return (USBD_INVAL);
+ ifc->device = dev;
+ ifc->idesc = idesc;
+ ifc->index = ifaceidx;
+ ifc->altindex = altidx;
+ nendpt = ifc->idesc->bNumEndpoints;
+ DPRINTFN(4,("usbd_fill_iface_data: found idesc nendpt=%d\n", nendpt));
+ if (nendpt != 0) {
+ ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint),
+ M_USB, M_NOWAIT);
+ if (ifc->endpoints == NULL)
+ return (USBD_NOMEM);
+ } else
+ ifc->endpoints = NULL;
+ ifc->priv = NULL;
+ p = (char *)ifc->idesc + ifc->idesc->bLength;
+ end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength);
+#define ed ((usb_endpoint_descriptor_t *)p)
+ for (endpt = 0; endpt < nendpt; endpt++) {
+ DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt));
+ for (; p < end; p += ed->bLength) {
+ DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p "
+ "len=%d type=%d\n",
+ p, end, ed->bLength, ed->bDescriptorType));
+ if (p + ed->bLength <= end && ed->bLength != 0 &&
+ ed->bDescriptorType == UDESC_ENDPOINT)
+ goto found;
+ if (ed->bLength == 0 ||
+ ed->bDescriptorType == UDESC_INTERFACE)
+ break;
+ }
+ /* passed end, or bad desc */
+ printf("usbd_fill_iface_data: bad descriptor(s): %s\n",
+ ed->bLength == 0 ? "0 length" :
+ ed->bDescriptorType == UDESC_INTERFACE ? "iface desc":
+ "out of data");
+ goto bad;
+ found:
+ ifc->endpoints[endpt].edesc = ed;
+ if (dev->speed == USB_SPEED_HIGH) {
+ u_int mps;
+ /* Control and bulk endpoints have max packet limits. */
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_CONTROL:
+ mps = USB_2_MAX_CTRL_PACKET;
+ goto check;
+ case UE_BULK:
+ mps = USB_2_MAX_BULK_PACKET;
+ check:
+ if (UGETW(ed->wMaxPacketSize) != mps) {
+ USETW(ed->wMaxPacketSize, mps);
+#ifdef DIAGNOSTIC
+ printf("usbd_fill_iface_data: bad max "
+ "packet size\n");
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ ifc->endpoints[endpt].refcnt = 0;
+ ifc->endpoints[endpt].savedtoggle = 0;
+ p += ed->bLength;
+ }
+#undef ed
+ LIST_INIT(&ifc->pipes);
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ if (ifc->endpoints != NULL) {
+ free(ifc->endpoints, M_USB);
+ ifc->endpoints = NULL;
+ }
+ return (USBD_INVAL);
+}
+
+void
+usbd_free_iface_data(usbd_device_handle dev, int ifcno)
+{
+ usbd_interface_handle ifc = &dev->ifaces[ifcno];
+ if (ifc->endpoints)
+ free(ifc->endpoints, M_USB);
+}
+
+static usbd_status
+usbd_set_config(usbd_device_handle dev, int conf)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_CONFIG;
+ USETW(req.wValue, conf);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_config_no(usbd_device_handle dev, int no, int msg)
+{
+ int index;
+ usb_config_descriptor_t cd;
+ usbd_status err;
+
+ if (no == USB_UNCONFIG_NO)
+ return (usbd_set_config_index(dev, USB_UNCONFIG_INDEX, msg));
+
+ DPRINTFN(5,("usbd_set_config_no: %d\n", no));
+ /* Figure out what config index to use. */
+ for (index = 0; index < dev->ddesc.bNumConfigurations; index++) {
+ err = usbd_get_config_desc(dev, index, &cd);
+ if (err)
+ return (err);
+ if (cd.bConfigurationValue == no)
+ return (usbd_set_config_index(dev, index, msg));
+ }
+ return (USBD_INVAL);
+}
+
+usbd_status
+usbd_set_config_index(usbd_device_handle dev, int index, int msg)
+{
+ usb_status_t ds;
+ usb_config_descriptor_t cd, *cdp;
+ usbd_status err;
+ int i, ifcidx, nifc, len, selfpowered, power;
+
+ DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index));
+
+ if (dev->config != USB_UNCONFIG_NO) {
+ nifc = dev->cdesc->bNumInterface;
+
+ /* Check that all interfaces are idle */
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
+ if (LIST_EMPTY(&dev->ifaces[ifcidx].pipes))
+ continue;
+ DPRINTF(("usbd_set_config_index: open pipes exist\n"));
+ return (USBD_IN_USE);
+ }
+
+ DPRINTF(("usbd_set_config_index: free old config\n"));
+ /* Free all configuration data structures. */
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++)
+ usbd_free_iface_data(dev, ifcidx);
+ free(dev->ifaces, M_USB);
+ free(dev->cdesc, M_USB);
+ dev->ifaces = NULL;
+ dev->cdesc = NULL;
+ dev->config = USB_UNCONFIG_NO;
+ }
+
+ if (index == USB_UNCONFIG_INDEX) {
+ /* We are unconfiguring the device, so leave unallocated. */
+ DPRINTF(("usbd_set_config_index: set config 0\n"));
+ err = usbd_set_config(dev, USB_UNCONFIG_NO);
+ if (err)
+ DPRINTF(("usbd_set_config_index: setting config=0 "
+ "failed, error=%s\n", usbd_errstr(err)));
+ return (err);
+ }
+
+ /* Get the short descriptor. */
+ err = usbd_get_config_desc(dev, index, &cd);
+ if (err)
+ return (err);
+ len = UGETW(cd.wTotalLength);
+ cdp = malloc(len, M_USB, M_NOWAIT);
+ if (cdp == NULL)
+ return (USBD_NOMEM);
+
+ /* Get the full descriptor. Try a few times for slow devices. */
+ for (i = 0; i < 3; i++) {
+ err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp);
+ if (!err)
+ break;
+ usbd_delay_ms(dev, 200);
+ }
+ if (err)
+ goto bad;
+ if (cdp->bDescriptorType != UDESC_CONFIG) {
+ DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n",
+ cdp->bDescriptorType));
+ err = USBD_INVAL;
+ goto bad;
+ }
+
+ /* Figure out if the device is self or bus powered. */
+ selfpowered = 0;
+ if (!(dev->quirks->uq_flags & UQ_BUS_POWERED) &&
+ (cdp->bmAttributes & UC_SELF_POWERED)) {
+ /* May be self powered. */
+ if (cdp->bmAttributes & UC_BUS_POWERED) {
+ /* Must ask device. */
+ if (dev->quirks->uq_flags & UQ_POWER_CLAIM) {
+ /*
+ * Hub claims to be self powered, but isn't.
+ * It seems that the power status can be
+ * determined by the hub characteristics.
+ */
+ usb_hub_descriptor_t hd;
+ usb_device_request_t req;
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
+ err = usbd_do_request(dev, &req, &hd);
+ if (!err &&
+ (UGETW(hd.wHubCharacteristics) &
+ UHD_PWR_INDIVIDUAL))
+ selfpowered = 1;
+ DPRINTF(("usbd_set_config_index: charac=0x%04x"
+ ", error=%s\n",
+ UGETW(hd.wHubCharacteristics),
+ usbd_errstr(err)));
+ } else {
+ err = usbd_get_device_status(dev, &ds);
+ if (!err &&
+ (UGETW(ds.wStatus) & UDS_SELF_POWERED))
+ selfpowered = 1;
+ DPRINTF(("usbd_set_config_index: status=0x%04x"
+ ", error=%s\n",
+ UGETW(ds.wStatus), usbd_errstr(err)));
+ }
+ } else
+ selfpowered = 1;
+ }
+ DPRINTF(("usbd_set_config_index: (addr %d) cno=%d attr=0x%02x, "
+ "selfpowered=%d, power=%d\n",
+ cdp->bConfigurationValue, dev->address, cdp->bmAttributes,
+ selfpowered, cdp->bMaxPower * 2));
+
+ /* Check if we have enough power. */
+#ifdef USB_DEBUG
+ if (dev->powersrc == NULL) {
+ DPRINTF(("usbd_set_config_index: No power source?\n"));
+ return (USBD_IOERROR);
+ }
+#endif
+ power = cdp->bMaxPower * 2;
+ if (power > dev->powersrc->power) {
+ DPRINTF(("power exceeded %d %d\n", power,dev->powersrc->power));
+ /* XXX print nicer message. */
+ if (msg)
+ device_printf(dev->bus->bdev,
+ "device addr %d (config %d) exceeds "
+ "power budget, %d mA > %d mA\n",
+ dev->address, cdp->bConfigurationValue,
+ power, dev->powersrc->power);
+ err = USBD_NO_POWER;
+ goto bad;
+ }
+ dev->power = power;
+ dev->self_powered = selfpowered;
+
+ /* Set the actual configuration value. */
+ DPRINTF(("usbd_set_config_index: set config %d\n",
+ cdp->bConfigurationValue));
+ err = usbd_set_config(dev, cdp->bConfigurationValue);
+ if (err) {
+ DPRINTF(("usbd_set_config_index: setting config=%d failed, "
+ "error=%s\n",
+ cdp->bConfigurationValue, usbd_errstr(err)));
+ goto bad;
+ }
+
+ /* Allocate and fill interface data. */
+ nifc = cdp->bNumInterface;
+ dev->ifaces = malloc(nifc * sizeof(struct usbd_interface),
+ M_USB, M_NOWAIT);
+ if (dev->ifaces == NULL) {
+ err = USBD_NOMEM;
+ goto bad;
+ }
+ DPRINTFN(5,("usbd_set_config_index: dev=%p cdesc=%p\n", dev, cdp));
+ dev->cdesc = cdp;
+ dev->config = cdp->bConfigurationValue;
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
+ err = usbd_fill_iface_data(dev, ifcidx, 0);
+ if (err) {
+ while (--ifcidx >= 0)
+ usbd_free_iface_data(dev, ifcidx);
+ goto bad;
+ }
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+
+ bad:
+ free(cdp, M_USB);
+ return (err);
+}
+
+/* XXX add function for alternate settings */
+
+usbd_status
+usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface,
+ struct usbd_endpoint *ep, int ival, usbd_pipe_handle *pipe)
+{
+ usbd_pipe_handle p;
+ usbd_status err;
+
+ DPRINTFN(1,("usbd_setup_pipe: dev=%p iface=%p ep=%p pipe=%p\n",
+ dev, iface, ep, pipe));
+ p = malloc(dev->bus->pipe_size, M_USB, M_NOWAIT);
+ if (p == NULL)
+ return (USBD_NOMEM);
+ p->device = dev;
+ p->iface = iface;
+ p->endpoint = ep;
+ ep->refcnt++;
+ p->refcnt = 1;
+ p->intrxfer = 0;
+ p->running = 0;
+ p->aborting = 0;
+ p->repeat = 0;
+ p->interval = ival;
+ STAILQ_INIT(&p->queue);
+ err = dev->bus->methods->open_pipe(p);
+ if (err) {
+ DPRINTFN(-1,("usbd_setup_pipe: endpoint=0x%x failed, error="
+ "%s\n",
+ ep->edesc->bEndpointAddress, usbd_errstr(err)));
+ free(p, M_USB);
+ return (err);
+ }
+
+ if (dev->quirks->uq_flags & UQ_OPEN_CLEARSTALL) {
+ /* Clear any stall and make sure DATA0 toggle will be used next. */
+ if (UE_GET_ADDR(ep->edesc->bEndpointAddress) != USB_CONTROL_ENDPOINT) {
+ err = usbd_clear_endpoint_stall(p);
+ if (err && err != USBD_STALLED && err != USBD_TIMEOUT) {
+ printf("usbd_setup_pipe: failed to start "
+ "endpoint, %s\n", usbd_errstr(err));
+ return (err);
+ }
+ }
+ }
+
+ *pipe = p;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Abort the device control pipe. */
+void
+usbd_kill_pipe(usbd_pipe_handle pipe)
+{
+ usbd_abort_pipe(pipe);
+ pipe->methods->close(pipe);
+ pipe->endpoint->refcnt--;
+ free(pipe, M_USB);
+}
+
+int
+usbd_getnewaddr(usbd_bus_handle bus)
+{
+ int addr;
+
+ for (addr = 1; addr < USB_MAX_DEVICES; addr++)
+ if (bus->devices[addr] == 0)
+ return (addr);
+ return (-1);
+}
+
+
+usbd_status
+usbd_probe_and_attach(device_t parent, usbd_device_handle dev,
+ int port, int addr)
+{
+ struct usb_attach_arg uaa;
+ usb_device_descriptor_t *dd = &dev->ddesc;
+ int found, i, confi, nifaces;
+ usbd_status err;
+ device_t *tmpdv;
+ usbd_interface_handle ifaces[256]; /* 256 is the absolute max */
+ char *devinfo;
+
+ /* XXX FreeBSD may leak resources on failure cases -- fixme */
+ device_t bdev;
+ struct usb_attach_arg *uaap;
+
+ devinfo = malloc(1024, M_USB, M_NOWAIT);
+ if (devinfo == NULL) {
+ device_printf(parent, "Can't allocate memory for probe string\n");
+ return (USBD_NOMEM);
+ }
+ bdev = device_add_child(parent, NULL, -1);
+ if (!bdev) {
+ free(devinfo, M_USB);
+ device_printf(parent, "Device creation failed\n");
+ return (USBD_INVAL);
+ }
+ uaap = malloc(sizeof(uaa), M_USB, M_NOWAIT);
+ if (uaap == NULL) {
+ free(devinfo, M_USB);
+ return (USBD_INVAL);
+ }
+ device_set_ivars(bdev, uaap);
+ uaa.device = dev;
+ uaa.iface = NULL;
+ uaa.ifaces = NULL;
+ uaa.nifaces = 0;
+ uaa.usegeneric = 0;
+ uaa.port = port;
+ uaa.configno = UHUB_UNK_CONFIGURATION;
+ uaa.ifaceno = UHUB_UNK_INTERFACE;
+ uaa.vendor = UGETW(dd->idVendor);
+ uaa.product = UGETW(dd->idProduct);
+ uaa.release = UGETW(dd->bcdDevice);
+ uaa.matchlvl = 0;
+
+ /* First try with device specific drivers. */
+ DPRINTF(("usbd_probe_and_attach: trying device specific drivers\n"));
+
+ dev->ifacenums = NULL;
+ dev->subdevs = malloc(2 * sizeof(device_t), M_USB, M_NOWAIT);
+ if (dev->subdevs == NULL) {
+ free(devinfo, M_USB);
+ return (USBD_NOMEM);
+ }
+ dev->subdevs[0] = bdev;
+ dev->subdevs[1] = 0;
+ *uaap = uaa;
+ usbd_devinfo(dev, 1, devinfo);
+ device_set_desc_copy(bdev, devinfo);
+ if (device_probe_and_attach(bdev) == 0) {
+ free(devinfo, M_USB);
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /*
+ * Free subdevs so we can reallocate it larger for the number of
+ * interfaces
+ */
+ tmpdv = dev->subdevs;
+ dev->subdevs = NULL;
+ free(tmpdv, M_USB);
+ DPRINTF(("usbd_probe_and_attach: no device specific driver found\n"));
+
+ DPRINTF(("usbd_probe_and_attach: looping over %d configurations\n",
+ dd->bNumConfigurations));
+ /* Next try with interface drivers. */
+ for (confi = 0; confi < dd->bNumConfigurations; confi++) {
+ DPRINTFN(1,("usbd_probe_and_attach: trying config idx=%d\n",
+ confi));
+ err = usbd_set_config_index(dev, confi, 1);
+ if (err) {
+#ifdef USB_DEBUG
+ DPRINTF(("%s: port %d, set config at addr %d failed, "
+ "error=%s\n", device_get_nameunit(parent), port,
+ addr, usbd_errstr(err)));
+#else
+ printf("%s: port %d, set config at addr %d failed\n",
+ device_get_nameunit(parent), port, addr);
+#endif
+ free(devinfo, M_USB);
+ return (err);
+ }
+ nifaces = dev->cdesc->bNumInterface;
+ uaa.configno = dev->cdesc->bConfigurationValue;
+ for (i = 0; i < nifaces; i++)
+ ifaces[i] = &dev->ifaces[i];
+ uaa.ifaces = ifaces;
+ uaa.nifaces = nifaces;
+ dev->subdevs = malloc((nifaces+1) * sizeof(device_t), M_USB,M_NOWAIT);
+ if (dev->subdevs == NULL) {
+ free(devinfo, M_USB);
+ return (USBD_NOMEM);
+ }
+ dev->ifacenums = malloc((nifaces) * sizeof(*dev->ifacenums),
+ M_USB,M_NOWAIT);
+ if (dev->ifacenums == NULL) {
+ free(devinfo, M_USB);
+ return (USBD_NOMEM);
+ }
+
+ found = 0;
+ for (i = 0; i < nifaces; i++) {
+ if (ifaces[i] == NULL)
+ continue; /* interface already claimed */
+ uaa.iface = ifaces[i];
+ uaa.ifaceno = ifaces[i]->idesc->bInterfaceNumber;
+ dev->subdevs[found] = bdev;
+ dev->subdevs[found + 1] = 0;
+ dev->ifacenums[found] = i;
+ *uaap = uaa;
+ usbd_devinfo(dev, 1, devinfo);
+ device_set_desc_copy(bdev, devinfo);
+ if (device_probe_and_attach(bdev) == 0) {
+ ifaces[i] = NULL; /* consumed */
+ found++;
+ /* create another child for the next iface */
+ bdev = device_add_child(parent, NULL, -1);
+ if (!bdev) {
+ device_printf(parent,
+ "Device add failed\n");
+ free(devinfo, M_USB);
+ return (USBD_NORMAL_COMPLETION);
+ }
+ uaap = malloc(sizeof(uaa), M_USB, M_NOWAIT);
+ if (uaap == NULL) {
+ free(devinfo, M_USB);
+ return (USBD_NOMEM);
+ }
+ device_set_ivars(bdev, uaap);
+ } else {
+ dev->subdevs[found] = 0;
+ }
+ }
+ if (found != 0) {
+ /* remove the last created child. It is unused */
+ free(uaap, M_USB);
+ free(devinfo, M_USB);
+ device_delete_child(parent, bdev);
+ /* free(uaap, M_USB); */ /* May be needed? xxx */
+ return (USBD_NORMAL_COMPLETION);
+ }
+ tmpdv = dev->subdevs;
+ dev->subdevs = NULL;
+ free(tmpdv, M_USB);
+ free(dev->ifacenums, M_USB);
+ dev->ifacenums = NULL;
+ }
+ /* No interfaces were attached in any of the configurations. */
+
+ if (dd->bNumConfigurations > 1) /* don't change if only 1 config */
+ usbd_set_config_index(dev, 0, 0);
+
+ DPRINTF(("usbd_probe_and_attach: no interface drivers found\n"));
+
+ /* Finally try the generic driver. */
+ uaa.iface = NULL;
+ uaa.usegeneric = 1;
+ uaa.configno = UHUB_UNK_CONFIGURATION;
+ uaa.ifaceno = UHUB_UNK_INTERFACE;
+ dev->subdevs = malloc(2 * sizeof(device_t), M_USB, M_NOWAIT);
+ if (dev->subdevs == 0) {
+ free(devinfo, M_USB);
+ return (USBD_NOMEM);
+ }
+ dev->subdevs[0] = bdev;
+ dev->subdevs[1] = 0;
+ *uaap = uaa;
+ usbd_devinfo(dev, 1, devinfo);
+ device_set_desc_copy(bdev, devinfo);
+ free(devinfo, M_USB);
+ if (device_probe_and_attach(bdev) == 0)
+ return (USBD_NORMAL_COMPLETION);
+
+ /*
+ * The generic attach failed, but leave the device as it is.
+ * We just did not find any drivers, that's all. The device is
+ * fully operational and not harming anyone.
+ */
+ DPRINTF(("usbd_probe_and_attach: generic attach failed\n"));
+ return (USBD_NORMAL_COMPLETION);
+}
+
+
+/*
+ * Called when a new device has been put in the powered state,
+ * but not yet in the addressed state.
+ * Get initial descriptor, set the address, get full descriptor,
+ * and attach a driver.
+ */
+usbd_status
+usbd_new_device(device_t parent, usbd_bus_handle bus, int depth,
+ int speed, int port, struct usbd_port *up)
+{
+ usbd_device_handle dev, adev;
+ struct usbd_device *hub;
+ usb_device_descriptor_t *dd;
+ usb_port_status_t ps;
+ usbd_status err;
+ int addr;
+ int i;
+ int p;
+
+ DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n",
+ bus, port, depth, speed));
+ addr = usbd_getnewaddr(bus);
+ if (addr < 0) {
+ device_printf(bus->bdev, "No free USB addresses\n");
+ return (USBD_NO_ADDR);
+ }
+
+ dev = malloc(sizeof *dev, M_USB, M_NOWAIT|M_ZERO);
+ if (dev == NULL)
+ return (USBD_NOMEM);
+
+ dev->bus = bus;
+
+ /* Set up default endpoint handle. */
+ dev->def_ep.edesc = &dev->def_ep_desc;
+
+ /* Set up default endpoint descriptor. */
+ dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE;
+ dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT;
+ dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
+ dev->def_ep_desc.bmAttributes = UE_CONTROL;
+ USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET);
+ dev->def_ep_desc.bInterval = 0;
+
+ dev->quirks = &usbd_no_quirk;
+ dev->address = USB_START_ADDR;
+ dev->ddesc.bMaxPacketSize = 0;
+ dev->depth = depth;
+ dev->powersrc = up;
+ dev->myhub = up->parent;
+
+ up->device = dev;
+
+ if (up->parent && speed > up->parent->speed) {
+#ifdef USB_DEBUG
+ printf("%s: maxium speed of attached "
+ "device, %d, is higher than speed "
+ "of parent HUB, %d.\n",
+ __FUNCTION__, speed, up->parent->speed);
+#endif
+ /*
+ * Reduce the speed, otherwise we won't setup the
+ * proper transfer methods.
+ */
+ speed = up->parent->speed;
+ }
+
+ /* Locate port on upstream high speed hub */
+ for (adev = dev, hub = up->parent;
+ hub != NULL && hub->speed != USB_SPEED_HIGH;
+ adev = hub, hub = hub->myhub)
+ ;
+ if (hub) {
+ for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) {
+ if (hub->hub->ports[p].device == adev) {
+ dev->myhsport = &hub->hub->ports[p];
+ goto found;
+ }
+ }
+ panic("usbd_new_device: cannot find HS port\n");
+ found:
+ DPRINTFN(1,("usbd_new_device: high speed port %d\n", p));
+ } else {
+ dev->myhsport = NULL;
+ }
+ dev->speed = speed;
+ dev->langid = USBD_NOLANG;
+ dev->cookie.cookie = ++usb_cookie_no;
+
+ /* Establish the default pipe. */
+ err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
+ &dev->default_pipe);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ dd = &dev->ddesc;
+ /* Try a few times in case the device is slow (i.e. outside specs.) */
+ DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr));
+ for (i = 0; i < 15; i++) {
+ /* Get the first 8 bytes of the device descriptor. */
+ err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd);
+ if (!err)
+ break;
+ usbd_delay_ms(dev, 200);
+ if ((i & 3) == 3) {
+ DPRINTFN(-1,("usb_new_device: set address %d "
+ "failed - trying a port reset\n", addr));
+ usbd_reset_port(up->parent, port, &ps);
+ }
+
+ }
+ if (err) {
+ DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc "
+ "failed\n", addr));
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ if (speed == USB_SPEED_HIGH) {
+ /* Max packet size must be 64 (sec 5.5.3). */
+ if (dd->bMaxPacketSize != USB_2_MAX_CTRL_PACKET) {
+#ifdef DIAGNOSTIC
+ printf("usbd_new_device: addr=%d bad max packet size\n",
+ addr);
+#endif
+ dd->bMaxPacketSize = USB_2_MAX_CTRL_PACKET;
+ }
+ }
+
+ DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, "
+ "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
+ addr,UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass,
+ dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength,
+ dev->speed));
+
+ if (dd->bDescriptorType != UDESC_DEVICE) {
+ /* Illegal device descriptor */
+ DPRINTFN(-1,("usbd_new_device: illegal descriptor %d\n",
+ dd->bDescriptorType));
+ usbd_remove_device(dev, up);
+ return (USBD_INVAL);
+ }
+
+ if (dd->bLength < USB_DEVICE_DESCRIPTOR_SIZE) {
+ DPRINTFN(-1,("usbd_new_device: bad length %d\n", dd->bLength));
+ usbd_remove_device(dev, up);
+ return (USBD_INVAL);
+ }
+
+ USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize);
+
+ /* Re-establish the default pipe with the new max packet size. */
+ usbd_kill_pipe(dev->default_pipe);
+ err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
+ &dev->default_pipe);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ err = usbd_reload_device_desc(dev);
+ if (err) {
+ DPRINTFN(-1, ("usbd_new_device: addr=%d, getting full desc "
+ "failed\n", addr));
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ /* Set the address */
+ DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr));
+ err = usbd_set_address(dev, addr);
+ if (err) {
+ DPRINTFN(-1,("usb_new_device: set address %d failed\n", addr));
+ err = USBD_SET_ADDR_FAILED;
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ /* Allow device time to set new address */
+ usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE);
+ dev->address = addr; /* New device address now */
+ bus->devices[addr] = dev;
+
+ /* Re-establish the default pipe with the new address. */
+ usbd_kill_pipe(dev->default_pipe);
+ err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
+ &dev->default_pipe);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ /* Assume 100mA bus powered for now. Changed when configured. */
+ dev->power = USB_MIN_POWER;
+ dev->self_powered = 0;
+
+ DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n",
+ addr, dev, parent));
+
+ err = usbd_probe_and_attach(parent, dev, port, addr);
+ if (err) {
+ usbd_remove_device(dev, up);
+ return (err);
+ }
+
+ usbd_add_dev_event(USB_EVENT_DEVICE_ATTACH, dev);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_reload_device_desc(usbd_device_handle dev)
+{
+ usbd_status err;
+ int i;
+
+ /* Get the full device descriptor. */
+ for (i = 0; i < 3; ++i) {
+ err = usbd_get_device_desc(dev, &dev->ddesc);
+ if (!err)
+ break;
+ usbd_delay_ms(dev, 200);
+ }
+ if (err)
+ return (err);
+
+ /* Figure out what's wrong with this device. */
+ dev->quirks = usbd_find_quirk(&dev->ddesc);
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usbd_remove_device(usbd_device_handle dev, struct usbd_port *up)
+{
+ DPRINTF(("usbd_remove_device: %p\n", dev));
+
+ if (dev->default_pipe != NULL)
+ usbd_kill_pipe(dev->default_pipe);
+ up->device = NULL;
+ dev->bus->devices[dev->address] = NULL;
+
+ free(dev, M_USB);
+}
+
+void
+usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di,
+ int usedev)
+{
+ struct usbd_port *p;
+ int i, err, s;
+
+ di->udi_bus = device_get_unit(dev->bus->bdev);
+ di->udi_addr = dev->address;
+ di->udi_cookie = dev->cookie;
+ usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product, usedev);
+ usbd_printBCD(di->udi_release, UGETW(dev->ddesc.bcdDevice));
+ di->udi_vendorNo = UGETW(dev->ddesc.idVendor);
+ di->udi_productNo = UGETW(dev->ddesc.idProduct);
+ di->udi_releaseNo = UGETW(dev->ddesc.bcdDevice);
+ di->udi_class = dev->ddesc.bDeviceClass;
+ di->udi_subclass = dev->ddesc.bDeviceSubClass;
+ di->udi_protocol = dev->ddesc.bDeviceProtocol;
+ di->udi_config = dev->config;
+ di->udi_power = dev->self_powered ? 0 : dev->power;
+ di->udi_speed = dev->speed;
+
+ if (dev->subdevs != NULL) {
+ for (i = 0; dev->subdevs[i] && i < USB_MAX_DEVNAMES; i++) {
+ if (device_is_attached(dev->subdevs[i]))
+ strlcpy(di->udi_devnames[i],
+ device_get_nameunit(dev->subdevs[i]),
+ USB_MAX_DEVNAMELEN);
+ else
+ di->udi_devnames[i][0] = 0;
+ }
+ } else {
+ i = 0;
+ }
+ for (/*i is set */; i < USB_MAX_DEVNAMES; i++)
+ di->udi_devnames[i][0] = 0; /* empty */
+
+ if (dev->hub) {
+ for (i = 0;
+ i < sizeof(di->udi_ports) / sizeof(di->udi_ports[0]) &&
+ i < dev->hub->hubdesc.bNbrPorts;
+ i++) {
+ p = &dev->hub->ports[i];
+ if (p->device)
+ err = p->device->address;
+ else {
+ s = UGETW(p->status.wPortStatus);
+ if (s & UPS_PORT_ENABLED)
+ err = USB_PORT_ENABLED;
+ else if (s & UPS_SUSPEND)
+ err = USB_PORT_SUSPENDED;
+ else if (s & UPS_PORT_POWER)
+ err = USB_PORT_POWERED;
+ else
+ err = USB_PORT_DISABLED;
+ }
+ di->udi_ports[i] = err;
+ }
+ di->udi_nports = dev->hub->hubdesc.bNbrPorts;
+ } else
+ di->udi_nports = 0;
+}
+
+void
+usb_free_device(usbd_device_handle dev)
+{
+ int ifcidx, nifc;
+
+ if (dev->default_pipe != NULL)
+ usbd_kill_pipe(dev->default_pipe);
+ if (dev->ifaces != NULL) {
+ nifc = dev->cdesc->bNumInterface;
+ for (ifcidx = 0; ifcidx < nifc; ifcidx++)
+ usbd_free_iface_data(dev, ifcidx);
+ free(dev->ifaces, M_USB);
+ }
+ if (dev->cdesc != NULL)
+ free(dev->cdesc, M_USB);
+ if (dev->subdevs != NULL)
+ free(dev->subdevs, M_USB);
+ if (dev->ifacenums != NULL)
+ free(dev->ifacenums, M_USB);
+ free(dev, M_USB);
+}
+
+/*
+ * The general mechanism for detaching drivers works as follows: Each
+ * driver is responsible for maintaining a reference count on the
+ * number of outstanding references to its softc (e.g. from
+ * processing hanging in a read or write). The detach method of the
+ * driver decrements this counter and flags in the softc that the
+ * driver is dying and then wakes any sleepers. It then sleeps on the
+ * softc. Each place that can sleep must maintain the reference
+ * count. When the reference count drops to -1 (0 is the normal value
+ * of the reference count) the a wakeup on the softc is performed
+ * signaling to the detach waiter that all references are gone.
+ */
+
+/*
+ * Called from process context when we discover that a port has
+ * been disconnected.
+ */
+void
+usb_disconnect_port(struct usbd_port *up, device_t parent)
+{
+ usbd_device_handle dev = up->device;
+ const char *hubname = device_get_nameunit(parent);
+ int i;
+
+ DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n",
+ up, dev, up->portno));
+
+#ifdef DIAGNOSTIC
+ if (dev == NULL) {
+ printf("usb_disconnect_port: no device\n");
+ return;
+ }
+#endif
+
+ if (dev->subdevs != NULL) {
+ DPRINTFN(3,("usb_disconnect_port: disconnect subdevs\n"));
+ for (i = 0; dev->subdevs[i]; i++) {
+ if (!device_is_quiet(dev->subdevs[i])) {
+ device_printf(dev->subdevs[i],
+ "at %s", hubname);
+ if (up->portno != 0)
+ printf(" port %d", up->portno);
+ printf(" (addr %d) disconnected\n", dev->address);
+ }
+
+ struct usb_attach_arg *uaap =
+ device_get_ivars(dev->subdevs[i]);
+ device_detach(dev->subdevs[i]);
+ free(uaap, M_USB);
+ device_delete_child(device_get_parent(dev->subdevs[i]),
+ dev->subdevs[i]);
+ dev->subdevs[i] = NULL;
+ }
+ }
+
+ usbd_add_dev_event(USB_EVENT_DEVICE_DETACH, dev);
+ dev->bus->devices[dev->address] = NULL;
+ up->device = NULL;
+ usb_free_device(dev);
+}
diff --git a/sys/legacy/dev/usb/usbcdc.h b/sys/legacy/dev/usb/usbcdc.h
new file mode 100644
index 0000000..d684108
--- /dev/null
+++ b/sys/legacy/dev/usb/usbcdc.h
@@ -0,0 +1,188 @@
+/* $NetBSD: usbcdc.h,v 1.9 2004/10/23 13:24:24 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+#ifndef _USBCDC_H_
+#define _USBCDC_H_
+
+#define UDESCSUB_CDC_HEADER 0
+#define UDESCSUB_CDC_CM 1 /* Call Management */
+#define UDESCSUB_CDC_ACM 2 /* Abstract Control Model */
+#define UDESCSUB_CDC_DLM 3 /* Direct Line Management */
+#define UDESCSUB_CDC_TRF 4 /* Telephone Ringer */
+#define UDESCSUB_CDC_TCLSR 5 /* Telephone Call ... */
+#define UDESCSUB_CDC_UNION 6
+#define UDESCSUB_CDC_CS 7 /* Country Selection */
+#define UDESCSUB_CDC_TOM 8 /* Telephone Operational Modes */
+#define UDESCSUB_CDC_USBT 9 /* USB Terminal */
+#define UDESCSUB_CDC_NCT 10
+#define UDESCSUB_CDC_PUF 11
+#define UDESCSUB_CDC_EUF 12
+#define UDESCSUB_CDC_MCMF 13
+#define UDESCSUB_CDC_CCMF 14
+#define UDESCSUB_CDC_ENF 15
+#define UDESCSUB_CDC_ANF 16
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uWord bcdCDC;
+} UPACKED usb_cdc_header_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmCapabilities;
+#define USB_CDC_CM_DOES_CM 0x01
+#define USB_CDC_CM_OVER_DATA 0x02
+ uByte bDataInterface;
+} UPACKED usb_cdc_cm_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmCapabilities;
+#define USB_CDC_ACM_HAS_FEATURE 0x01
+#define USB_CDC_ACM_HAS_LINE 0x02
+#define USB_CDC_ACM_HAS_BREAK 0x04
+#define USB_CDC_ACM_HAS_NETWORK_CONN 0x08
+} UPACKED usb_cdc_acm_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bMasterInterface;
+ uByte bSlaveInterface[1];
+} UPACKED usb_cdc_union_descriptor_t;
+
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte iMacAddress;
+ uDWord bmEthernetStatistics;
+ uWord wMaxSegmentSize;
+ uWord wNumberMCFikters;
+ uByte bNumberPowerFilters;
+} UPACKED usb_cdc_ethernet_descriptor_t;
+
+#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
+#define UCDC_GET_ENCAPSULATED_RESPONSE 0x01
+#define UCDC_SET_COMM_FEATURE 0x02
+#define UCDC_GET_COMM_FEATURE 0x03
+#define UCDC_ABSTRACT_STATE 0x01
+#define UCDC_COUNTRY_SETTING 0x02
+#define UCDC_CLEAR_COMM_FEATURE 0x04
+#define UCDC_SET_LINE_CODING 0x20
+#define UCDC_GET_LINE_CODING 0x21
+#define UCDC_SET_CONTROL_LINE_STATE 0x22
+#define UCDC_LINE_DTR 0x0001
+#define UCDC_LINE_RTS 0x0002
+#define UCDC_SEND_BREAK 0x23
+#define UCDC_BREAK_ON 0xffff
+#define UCDC_BREAK_OFF 0x0000
+
+typedef struct {
+ uWord wState;
+#define UCDC_IDLE_SETTING 0x0001
+#define UCDC_DATA_MULTIPLEXED 0x0002
+} UPACKED usb_cdc_abstract_state_t;
+#define UCDC_ABSTRACT_STATE_LENGTH 2
+
+typedef struct {
+ uDWord dwDTERate;
+ uByte bCharFormat;
+#define UCDC_STOP_BIT_1 0
+#define UCDC_STOP_BIT_1_5 1
+#define UCDC_STOP_BIT_2 2
+ uByte bParityType;
+#define UCDC_PARITY_NONE 0
+#define UCDC_PARITY_ODD 1
+#define UCDC_PARITY_EVEN 2
+#define UCDC_PARITY_MARK 3
+#define UCDC_PARITY_SPACE 4
+ uByte bDataBits;
+} UPACKED usb_cdc_line_state_t;
+#define UCDC_LINE_STATE_LENGTH 7
+
+typedef struct {
+ uByte bmRequestType;
+#define UCDC_NOTIFICATION 0xa1
+ uByte bNotification;
+#define UCDC_N_NETWORK_CONNECTION 0x00
+#define UCDC_N_RESPONSE_AVAILABLE 0x01
+#define UCDC_N_AUX_JACK_HOOK_STATE 0x08
+#define UCDC_N_RING_DETECT 0x09
+#define UCDC_N_SERIAL_STATE 0x20
+#define UCDC_N_CALL_STATE_CHANGED 0x28
+#define UCDC_N_LINE_STATE_CHANGED 0x29
+#define UCDC_N_CONNECTION_SPEED_CHANGE 0x2a
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+ uByte data[16];
+} UPACKED usb_cdc_notification_t;
+#define UCDC_NOTIFICATION_LENGTH 8
+
+/*
+ * Bits set in the SERIAL STATE notifcation (first byte of data)
+ */
+
+#define UCDC_N_SERIAL_OVERRUN 0x40
+#define UCDC_N_SERIAL_PARITY 0x20
+#define UCDC_N_SERIAL_FRAMING 0x10
+#define UCDC_N_SERIAL_RI 0x08
+#define UCDC_N_SERIAL_BREAK 0x04
+#define UCDC_N_SERIAL_DSR 0x02
+#define UCDC_N_SERIAL_DCD 0x01
+
+/* Serial state bit masks */
+#define UCDC_MDM_RXCARRIER 0x01
+#define UCDC_MDM_TXCARRIER 0x02
+#define UCDC_MDM_BREAK 0x04
+#define UCDC_MDM_RING 0x08
+#define UCDC_MDM_FRAMING_ERR 0x10
+#define UCDC_MDM_PARITY_ERR 0x20
+#define UCDC_MDM_OVERRUN_ERR 0x40
+
+#endif /* _USBCDC_H_ */
diff --git a/sys/legacy/dev/usb/usbdevs b/sys/legacy/dev/usb/usbdevs
new file mode 100644
index 0000000..0a3d85e
--- /dev/null
+++ b/sys/legacy/dev/usb/usbdevs
@@ -0,0 +1,2527 @@
+$FreeBSD$
+/* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */
+
+/*-
+ * Copyright (c) 1998-2004 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.
+ */
+
+/*
+ * List of known USB vendors
+ *
+ * USB.org publishes a VID list of USB-IF member companies at
+ * http://www.usb.org/developers/tools
+ * Note that it does not show companies that have obtained a Vendor ID
+ * without becoming full members.
+ *
+ * Please note that these IDs do not do anything. Adding an ID here and
+ * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name
+ * available to the source code and does not change any functionality, nor
+ * does it make your device available to a specific driver.
+ * It will however make the descriptive string available if a device does not
+ * provide the string itself.
+ *
+ * After adding a vendor ID VNDR and a product ID PRDCT you will have the
+ * following extra defines:
+ * #define USB_VENDOR_VNDR 0x????
+ * #define USB_PRODUCT_VNDR_PRDCT 0x????
+ *
+ * You may have to add these defines to the respective probe routines to
+ * make the device recognised by the appropriate device driver.
+ */
+
+vendor UNKNOWN1 0x0053 Unknown vendor
+vendor UNKNOWN2 0x0105 Unknown vendor
+vendor EGALAX2 0x0123 eGalax, Inc.
+vendor HUMAX 0x02ad HUMAX
+vendor LTS 0x0386 LTS
+vendor BWCT 0x03da Bernd Walter Computer Technology
+vendor AOX 0x03e8 AOX
+vendor THESYS 0x03e9 Thesys
+vendor DATABROADCAST 0x03ea Data Broadcasting
+vendor ATMEL 0x03eb Atmel
+vendor IWATSU 0x03ec Iwatsu America
+vendor MITSUMI 0x03ee Mitsumi
+vendor HP 0x03f0 Hewlett Packard
+vendor GENOA 0x03f1 Genoa
+vendor OAK 0x03f2 Oak
+vendor ADAPTEC 0x03f3 Adaptec
+vendor DIEBOLD 0x03f4 Diebold
+vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical
+vendor EPSONIMAGING 0x03f8 Epson Imaging
+vendor KEYTRONIC 0x03f9 KeyTronic
+vendor OPTI 0x03fb OPTi
+vendor ELITEGROUP 0x03fc Elitegroup
+vendor XILINX 0x03fd Xilinx
+vendor FARALLON 0x03fe Farallon Communications
+vendor NATIONAL 0x0400 National Semiconductor
+vendor NATIONALREG 0x0401 National Registry
+vendor ACERLABS 0x0402 Acer Labs
+vendor FTDI 0x0403 Future Technology Devices
+vendor NCR 0x0404 NCR
+vendor SYNOPSYS2 0x0405 Synopsys
+vendor FUJITSUICL 0x0406 Fujitsu-ICL
+vendor FUJITSU2 0x0407 Fujitsu Personal Systems
+vendor QUANTA 0x0408 Quanta
+vendor NEC 0x0409 NEC
+vendor KODAK 0x040a Eastman Kodak
+vendor WELTREND 0x040b Weltrend
+vendor VIA 0x040d VIA
+vendor MCCI 0x040e MCCI
+vendor MELCO 0x0411 Melco
+vendor LEADTEK 0x0413 Leadtek
+vendor WINBOND 0x0416 Winbond
+vendor PHOENIX 0x041a Phoenix
+vendor CREATIVE 0x041e Creative Labs
+vendor NOKIA 0x0421 Nokia
+vendor ADI 0x0422 ADI Systems
+vendor CATC 0x0423 Computer Access Technology
+vendor SMC2 0x0424 Standard Microsystems
+vendor MOTOROLA_HK 0x0425 Motorola HK
+vendor GRAVIS 0x0428 Advanced Gravis Computer
+vendor CIRRUSLOGIC 0x0429 Cirrus Logic
+vendor INNOVATIVE 0x042c Innovative Semiconductors
+vendor MOLEX 0x042f Molex
+vendor SUN 0x0430 Sun Microsystems
+vendor UNISYS 0x0432 Unisys
+vendor TAUGA 0x0436 Taugagreining HF
+vendor AMD 0x0438 Advanced Micro Devices
+vendor LEXMARK 0x043d Lexmark International
+vendor LG 0x043e LG Electronics
+vendor NANAO 0x0440 NANAO
+vendor GATEWAY 0x0443 Gateway 2000
+vendor NMB 0x0446 NMB
+vendor ALPS 0x044e Alps Electric
+vendor THRUST 0x044f Thrustmaster
+vendor TI 0x0451 Texas Instruments
+vendor ANALOGDEVICES 0x0456 Analog Devices
+vendor SIS 0x0457 Silicon Integrated Systems Corp.
+vendor KYE 0x0458 KYE Systems
+vendor DIAMOND2 0x045a Diamond (Supra)
+vendor RENESAS 0x045b Renesas
+vendor MICROSOFT 0x045e Microsoft
+vendor PRIMAX 0x0461 Primax Electronics
+vendor MGE 0x0463 MGE UPS Systems
+vendor AMP 0x0464 AMP
+vendor CHERRY 0x046a Cherry Mikroschalter
+vendor MEGATRENDS 0x046b American Megatrends
+vendor LOGITECH 0x046d Logitech
+vendor BTC 0x046e Behavior Tech. Computer
+vendor PHILIPS 0x0471 Philips
+vendor SUN2 0x0472 Sun Microsystems (offical)
+vendor SANYO 0x0474 Sanyo Electric
+vendor SEAGATE 0x0477 Seagate
+vendor CONNECTIX 0x0478 Connectix
+vendor SEMTECH 0x047a Semtech
+vendor KENSINGTON 0x047d Kensington
+vendor LUCENT 0x047e Lucent
+vendor PLANTRONICS 0x047f Plantronics
+vendor KYOCERA 0x0482 Kyocera Wireless Corp.
+vendor STMICRO 0x0483 STMicroelectronics
+vendor FOXCONN 0x0489 Foxconn
+vendor MEIZU 0x0492 Meizu Electronics
+vendor YAMAHA 0x0499 YAMAHA
+vendor COMPAQ 0x049f Compaq
+vendor HITACHI 0x04a4 Hitachi
+vendor ACERP 0x04a5 Acer Peripherals
+vendor DAVICOM 0x04a6 Davicom
+vendor VISIONEER 0x04a7 Visioneer
+vendor CANON 0x04a9 Canon
+vendor NIKON 0x04b0 Nikon
+vendor PAN 0x04b1 Pan International
+vendor IBM 0x04b3 IBM
+vendor CYPRESS 0x04b4 Cypress Semiconductor
+vendor ROHM 0x04b5 ROHM
+vendor COMPAL 0x04b7 Compal
+vendor EPSON 0x04b8 Seiko Epson
+vendor RAINBOW 0x04b9 Rainbow Technologies
+vendor IODATA 0x04bb I-O Data
+vendor TDK 0x04bf TDK
+vendor 3COMUSR 0x04c1 U.S. Robotics
+vendor METHODE 0x04c2 Methode Electronics Far East
+vendor MAXISWITCH 0x04c3 Maxi Switch
+vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research
+vendor FUJITSU 0x04c5 Fujitsu
+vendor TOSHIBAAM 0x04c6 Toshiba America
+vendor MICROMACRO 0x04c7 Micro Macro Technologies
+vendor KONICA 0x04c8 Konica
+vendor LITEON 0x04ca Lite-On Technology
+vendor FUJIPHOTO 0x04cb Fuji Photo Film
+vendor PHILIPSSEMI 0x04cc Philips Semiconductors
+vendor TATUNG 0x04cd Tatung Co. Of America
+vendor SCANLOGIC 0x04ce ScanLogic
+vendor MYSON 0x04cf Myson Technology
+vendor DIGI2 0x04d0 Digi
+vendor ITTCANON 0x04d1 ITT Canon
+vendor ALTEC 0x04d2 Altec Lansing
+vendor LSI 0x04d4 LSI
+vendor MENTORGRAPHICS 0x04d6 Mentor Graphics
+vendor ITUNERNET 0x04d8 I-Tuner Networks
+vendor HOLTEK 0x04d9 Holtek Semiconductor, Inc.
+vendor PANASONIC 0x04da Panasonic (Matsushita)
+vendor HUANHSIN 0x04dc Huan Hsin
+vendor SHARP 0x04dd Sharp
+vendor IIYAMA 0x04e1 Iiyama
+vendor SHUTTLE 0x04e6 Shuttle Technology
+vendor ELO 0x04e7 Elo TouchSystems
+vendor SAMSUNG 0x04e8 Samsung Electronics
+vendor NORTHSTAR 0x04eb Northstar
+vendor TOKYOELECTRON 0x04ec Tokyo Electron
+vendor ANNABOOKS 0x04ed Annabooks
+vendor JVC 0x04f1 JVC
+vendor CHICONY 0x04f2 Chicony Electronics
+vendor ELAN 0x04f3 Elan
+vendor NEWNEX 0x04f7 Newnex
+vendor BROTHER 0x04f9 Brother Industries
+vendor DALLAS 0x04fa Dallas Semiconductor
+vendor AIPTEK2 0x04fc AIPTEK International
+vendor PFU 0x04fe PFU
+vendor FUJIKURA 0x0501 Fujikura/DDK
+vendor ACER 0x0502 Acer
+vendor 3COM 0x0506 3Com
+vendor HOSIDEN 0x0507 Hosiden Corporation
+vendor AZTECH 0x0509 Aztech Systems
+vendor BELKIN 0x050d Belkin Components
+vendor KAWATSU 0x050f Kawatsu Semiconductor
+vendor FCI 0x0514 FCI
+vendor LONGWELL 0x0516 Longwell
+vendor COMPOSITE 0x0518 Composite
+vendor STAR 0x0519 Star Micronics
+vendor APC 0x051d American Power Conversion
+vendor SCIATLANTA 0x051e Scientific Atlanta
+vendor TSM 0x0520 TSM
+vendor CONNECTEK 0x0522 Advanced Connectek USA
+vendor NETCHIP 0x0525 NetChip Technology
+vendor ALTRA 0x0527 ALTRA
+vendor ATI 0x0528 ATI Technologies
+vendor AKS 0x0529 Aladdin Knowledge Systems
+vendor TEKOM 0x052b Tekom
+vendor CANONDEV 0x052c Canon
+vendor WACOMTECH 0x0531 Wacom
+vendor INVENTEC 0x0537 Inventec
+vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals
+vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG
+vendor SYNOPSYS 0x053f Synopsys
+vendor UNIACCESS 0x0540 Universal Access
+vendor VIEWSONIC 0x0543 ViewSonic
+vendor XIRLINK 0x0545 Xirlink
+vendor ANCHOR 0x0547 Anchor Chips
+vendor SONY 0x054c Sony
+vendor FUJIXEROX 0x0550 Fuji Xerox
+vendor VISION 0x0553 VLSI Vision
+vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems
+vendor ATEN 0x0557 ATEN International
+vendor SAMSUNG2 0x055d Samsung Electronics
+vendor MUSTEK 0x055f Mustek Systems
+vendor TELEX 0x0562 Telex Communications
+vendor CHINON 0x0564 Chinon
+vendor PERACOM 0x0565 Peracom Networks
+vendor ALCOR2 0x0566 Alcor Micro
+vendor XYRATEX 0x0567 Xyratex
+vendor WACOM 0x056a WACOM
+vendor ETEK 0x056c e-TEK Labs
+vendor EIZO 0x056d EIZO
+vendor ELECOM 0x056e Elecom
+vendor CONEXANT 0x0572 Conexant
+vendor HAUPPAUGE 0x0573 Hauppauge Computer Works
+vendor BAFO 0x0576 BAFO/Quality Computer Accessories
+vendor YEDATA 0x057b Y-E Data
+vendor AVM 0x057c AVM
+vendor QUICKSHOT 0x057f Quickshot
+vendor ROLAND 0x0582 Roland
+vendor ROCKFIRE 0x0583 Rockfire
+vendor RATOC 0x0584 RATOC Systems
+vendor ZYXEL 0x0586 ZyXEL Communication
+vendor INFINEON 0x058b Infineon
+vendor MICREL 0x058d Micrel
+vendor ALCOR 0x058f Alcor Micro
+vendor OMRON 0x0590 OMRON
+vendor ZORAN 0x0595 Zoran Microelectronics
+vendor NIIGATA 0x0598 Niigata
+vendor IOMEGA 0x059b Iomega
+vendor ATREND 0x059c A-Trend Technology
+vendor AID 0x059d Advanced Input Devices
+vendor LACIE 0x059f LaCie
+vendor FUJIFILM 0x05a2 Fuji Film
+vendor ARC 0x05a3 ARC
+vendor ORTEK 0x05a4 Ortek
+vendor BOSE 0x05a7 Bose
+vendor OMNIVISION 0x05a9 OmniVision
+vendor INSYSTEM 0x05ab In-System Design
+vendor APPLE 0x05ac Apple Computer
+vendor YCCABLE 0x05ad Y.C. Cable
+vendor DIGITALPERSONA 0x05ba DigitalPersona
+vendor 3G 0x05bc 3G Green Green Globe
+vendor RAFI 0x05bd RAFI
+vendor TYCO 0x05be Tyco
+vendor KAWASAKI 0x05c1 Kawasaki
+vendor DIGI 0x05c5 Digi International
+vendor QUALCOMM2 0x05c6 Qualcomm
+vendor QTRONIX 0x05c7 Qtronix
+vendor FOXLINK 0x05c8 Foxlink
+vendor RICOH 0x05ca Ricoh
+vendor ELSA 0x05cc ELSA
+vendor SCIWORX 0x05ce sci-worx
+vendor BRAINBOXES 0x05d1 Brainboxes Limited
+vendor ULTIMA 0x05d8 Ultima
+vendor AXIOHM 0x05d9 Axiohm Transaction Solutions
+vendor MICROTEK 0x05da Microtek
+vendor SUNTAC 0x05db SUN Corporation
+vendor LEXAR 0x05dc Lexar Media
+vendor ADDTRON 0x05dd Addtron
+vendor SYMBOL 0x05e0 Symbol Technologies
+vendor SYNTEK 0x05e1 Syntek
+vendor GENESYS 0x05e3 Genesys Logic
+vendor FUJI 0x05e5 Fuji Electric
+vendor KEITHLEY 0x05e6 Keithley Instruments
+vendor EIZONANAO 0x05e7 EIZO Nanao
+vendor KLSI 0x05e9 Kawasaki LSI
+vendor FFC 0x05eb FFC
+vendor ANKO 0x05ef Anko Electronic
+vendor PIENGINEERING 0x05f3 P.I. Engineering
+vendor AOC 0x05f6 AOC International
+vendor CHIC 0x05fe Chic Technology
+vendor BARCO 0x0600 Barco Display Systems
+vendor BRIDGE 0x0607 Bridge Information
+vendor SOLIDYEAR 0x060b Solid Year
+vendor BIORAD 0x0614 Bio-Rad Laboratories
+vendor MACALLY 0x0618 Macally
+vendor ACTLABS 0x061c Act Labs
+vendor ALARIS 0x0620 Alaris
+vendor APEX 0x0624 Apex
+vendor CREATIVE3 0x062a Creative Labs
+vendor VIVITAR 0x0636 Vivitar
+vendor GUNZE 0x0637 Gunze Electronics USA
+vendor AVISION 0x0638 Avision
+vendor TEAC 0x0644 TEAC
+vendor SGI 0x065e Silicon Graphics
+vendor SANWASUPPLY 0x0663 Sanwa Supply
+vendor LINKSYS 0x066b Linksys
+vendor ACERSA 0x066e Acer Semiconductor America
+vendor SIGMATEL 0x066f Sigmatel
+vendor DRAYTEK 0x0675 DrayTek
+vendor AIWA 0x0677 Aiwa
+vendor ACARD 0x0678 ACARD Technology
+vendor PROLIFIC 0x067b Prolific Technology
+vendor SIEMENS 0x067c Siemens
+vendor AVANCELOGIC 0x0680 Avance Logic
+vendor SIEMENS2 0x0681 Siemens
+vendor MINOLTA 0x0686 Minolta
+vendor CHPRODUCTS 0x068e CH Products
+vendor HAGIWARA 0x0693 Hagiwara Sys-Com
+vendor CTX 0x0698 Chuntex
+vendor ASKEY 0x069a Askey Computer
+vendor SAITEK 0x06a3 Saitek
+vendor ALCATELT 0x06b9 Alcatel Telecom
+vendor AGFA 0x06bd AGFA-Gevaert
+vendor ASIAMD 0x06be Asia Microelectronic Development
+vendor BIZLINK 0x06c4 Bizlink International
+vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc.
+vendor AASHIMA 0x06d6 Aashima Technology
+vendor MULTITECH 0x06e0 MultiTech
+vendor ADS 0x06e1 ADS Technologies
+vendor ALCATELM 0x06e4 Alcatel Microelectronics
+vendor SIRIUS 0x06ea Sirius Technologies
+vendor GUILLEMOT 0x06f8 Guillemot
+vendor BOSTON 0x06fd Boston Acoustics
+vendor SMC 0x0707 Standard Microsystems
+vendor PUTERCOM 0x0708 Putercom
+vendor MCT 0x0711 MCT
+vendor IMATION 0x0718 Imation
+vendor SONYERICSSON 0x0731 Sony Ericsson
+vendor EICON 0x0734 Eicon Networks
+vendor SYNTECH 0x0745 Syntech Information
+vendor DIGITALSTREAM 0x074e Digital Stream
+vendor AUREAL 0x0755 Aureal Semiconductor
+vendor MIDIMAN 0x0763 Midiman
+vendor CYBERPOWER 0x0764 Cyber Power Systems, Inc.
+vendor SURECOM 0x0769 Surecom Technology
+vendor LINKSYS2 0x077b Linksys
+vendor GRIFFIN 0x077d Griffin Technology
+vendor SANDISK 0x0781 SanDisk
+vendor JENOPTIK 0x0784 Jenoptik
+vendor LOGITEC 0x0789 Logitec
+vendor BRIMAX 0x078e Brimax
+vendor AXIS 0x0792 Axis Communications
+vendor ABL 0x0794 ABL Electronics
+vendor SAGEM 0x079b Sagem
+vendor SUNCOMM 0x079c Sun Communications, Inc.
+vendor ALFADATA 0x079d Alfadata Computer
+vendor NATIONALTECH 0x07a2 National Technical Systems
+vendor ONNTO 0x07a3 Onnto
+vendor BE 0x07a4 Be
+vendor ADMTEK 0x07a6 ADMtek
+vendor COREGA 0x07aa Corega
+vendor FREECOM 0x07ab Freecom
+vendor MICROTECH 0x07af Microtech
+vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola)
+vendor OLYMPUS 0x07b4 Olympus
+vendor ABOCOM 0x07b8 AboCom Systems
+vendor KEISOKUGIKEN 0x07c1 Keisokugiken
+vendor ONSPEC 0x07c4 OnSpec
+vendor APG 0x07c5 APG Cash Drawer
+vendor BUG 0x07c8 B.U.G.
+vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International
+vendor AVERMEDIA 0x07ca AVerMedia Technologies
+vendor SIIG 0x07cc SIIG
+vendor CASIO 0x07cf CASIO
+vendor DLINK2 0x07d1 D-Link
+vendor APTIO 0x07d2 Aptio Products
+vendor ARASAN 0x07da Arasan Chip Systems
+vendor ALLIEDCABLE 0x07e6 Allied Cable
+vendor STSN 0x07ef STSN
+vendor CENTURY 0x07f7 Century Corp
+vendor ZOOM 0x0803 Zoom Telephonics
+vendor PCS 0x0810 Personal Communication Systems
+vendor BROADLOGIC 0x0827 BroadLogic
+vendor HANDSPRING 0x082d Handspring
+vendor PALM 0x0830 Palm Computing
+vendor SOURCENEXT 0x0833 SOURCENEXT
+vendor ACTIONSTAR 0x0835 Action Star Enterprise
+vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin
+vendor ACCTON 0x083a Accton Technology
+vendor DIAMOND 0x0841 Diamond
+vendor NETGEAR 0x0846 BayNETGEAR
+vendor TOPRE 0x0853 Topre Corporation
+vendor ACTIVEWIRE 0x0854 ActiveWire
+vendor BBELECTRONICS 0x0856 B&B Electronics
+vendor PORTGEAR 0x085a PortGear
+vendor NETGEAR2 0x0864 Netgear
+vendor SYSTEMTALKS 0x086e System Talks
+vendor METRICOM 0x0870 Metricom
+vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America
+vendor JATON 0x087d Jaton
+vendor APT 0x0880 APT Technologies
+vendor BOCARESEARCH 0x0885 Boca Research
+vendor ANDREA 0x08a8 Andrea Electronics
+vendor BURRBROWN 0x08bb Burr-Brown Japan
+vendor 2WIRE 0x08c8 2Wire
+vendor AIPTEK 0x08ca AIPTEK International
+vendor SMARTBRIDGES 0x08d1 SmartBridges
+vendor BILLIONTON 0x08dd Billionton Systems
+vendor EXTENDED 0x08e9 Extended Systems
+vendor MSYSTEMS 0x08ec M-Systems
+vendor AUTHENTEC 0x08ff AuthenTec
+vendor AUDIOTECHNICA 0x0909 Audio-Technica
+vendor TRUMPION 0x090a Trumpion Microelectronics
+vendor FEIYA 0x090c Feiya
+vendor ALATION 0x0910 Alation Systems
+vendor GLOBESPAN 0x0915 Globespan
+vendor CONCORDCAMERA 0x0919 Concord Camera
+vendor GARMIN 0x091e Garmin International
+vendor GOHUBS 0x0921 GoHubs
+vendor XEROX 0x0924 Xerox
+vendor BIOMETRIC 0x0929 American Biometric Company
+vendor TOSHIBA 0x0930 Toshiba
+vendor PLEXTOR 0x093b Plextor
+vendor INTREPIDCS 0x093c Intrepid
+vendor YANO 0x094f Yano
+vendor KINGSTON 0x0951 Kingston Technology
+vendor BLUEWATER 0x0956 BlueWater Systems
+vendor AGILENT 0x0957 Agilent Technologies
+vendor GUDE 0x0959 Gude ADS
+vendor PORTSMITH 0x095a Portsmith
+vendor ACERW 0x0967 Acer
+vendor ADIRONDACK 0x0976 Adirondack Wire & Cable
+vendor BECKHOFF 0x0978 Beckhoff
+vendor MINDSATWORK 0x097a Minds At Work
+vendor POINTCHIPS 0x09a6 PointChips
+vendor INTERSIL 0x09aa Intersil
+vendor ALTIUS 0x09b3 Altius Solutions
+vendor ARRIS 0x09c1 Arris Interactive
+vendor ACTIVCARD 0x09c3 ACTIVCARD
+vendor ACTISYS 0x09c4 ACTiSYS
+vendor NOVATEL2 0x09d7 Novatel Wireless
+vendor AFOURTECH 0x09da A-FOUR TECH
+vendor AIMEX 0x09dc AIMEX
+vendor ADDONICS 0x09df Addonics Technologies
+vendor AKAI 0x09e8 AKAI professional M.I.
+vendor ARESCOM 0x09f5 ARESCOM
+vendor BAY 0x09f9 Bay Associates
+vendor ALTERA 0x09fb Altera
+vendor CSR 0x0a12 Cambridge Silicon Radio
+vendor TREK 0x0a16 Trek Technology
+vendor ASAHIOPTICAL 0x0a17 Asahi Optical
+vendor BOCASYSTEMS 0x0a43 Boca Systems
+vendor SHANTOU 0x0a46 ShanTou
+vendor MEDIAGEAR 0x0a48 MediaGear
+vendor BROADCOM 0x0a5c Broadcom
+vendor GREENHOUSE 0x0a6b GREENHOUSE
+vendor GEOCAST 0x0a79 Geocast Network Systems
+vendor IDQUANTIQUE 0x0aba id Quantique
+vendor ZYDAS 0x0ace Zydas Technology Corporation
+vendor NEODIO 0x0aec Neodio
+vendor OPTION 0x0af0 Option N.V:
+vendor ASUS 0x0b05 ASUSTeK Computer
+vendor TODOS 0x0b0c Todos Data System
+vendor SIIG2 0x0b39 SIIG
+vendor TEKRAM 0x0b3b Tekram Technology
+vendor HAL 0x0b41 HAL Corporation
+vendor EMS 0x0b43 EMS Production
+vendor NEC2 0x0b62 NEC
+vendor ATI2 0x0b6f ATI
+vendor ZEEVO 0x0b7a Zeevo, Inc.
+vendor KURUSUGAWA 0x0b7e Kurusugawa Electronics, Inc.
+vendor ASIX 0x0b95 ASIX Electronics
+vendor O2MICRO 0x0b97 O2 Micro, Inc.
+vendor USR 0x0baf U.S. Robotics
+vendor AMBIT 0x0bb2 Ambit Microsystems
+vendor HTC 0x0bb4 HTC
+vendor REALTEK 0x0bda Realtek
+vendor ADDONICS2 0x0bf6 Addonics Technology
+vendor FSC 0x0bf8 Fujitsu Siemens Computers
+vendor AGATE 0x0c08 Agate Technologies
+vendor DMI 0x0c0b DMI
+vendor CHICONY2 0x0c45 Chicony
+vendor SEALEVEL 0x0c52 Sealevel System
+vendor LUWEN 0x0c76 Luwen
+vendor KYOCERA2 0x0c88 Kyocera Wireless Corp.
+vendor ZCOM 0x0cde Z-Com
+vendor ATHEROS2 0x0cf3 Atheros Communications
+vendor TANGTOP 0x0d3d Tangtop
+vendor SMC3 0x0d5c Standard Microsystems
+vendor ADDON 0x0d7d Add-on Technology
+vendor ACDC 0x0d7e American Computer & Digital Components
+vendor ABC 0x0d8c ABC
+vendor CONCEPTRONIC 0x0d8e Conceptronic
+vendor SKANHEX 0x0d96 Skanhex Technology, Inc.
+vendor MSI 0x0db0 Micro Star International
+vendor ELCON 0x0db7 ELCON Systemtechnik
+vendor NETAC 0x0dd8 Netac
+vendor SITECOMEU 0x0df6 Sitecom Europe
+vendor MOBILEACTION 0x0df7 Mobile Action
+vendor SPEEDDRAGON 0x0e55 Speed Dragon Multimedia
+vendor HAWKING 0x0e66 Hawking
+vendor FOSSIL 0x0e67 Fossil, Inc
+vendor GMATE 0x0e7e G.Mate, Inc
+vendor OTI 0x0ea0 Ours Technology
+vendor YISO 0x0eab Yiso Wireless Co.
+vendor PILOTECH 0x0eaf Pilotech
+vendor NOVATECH 0x0eb0 NovaTech
+vendor ITEGNO 0x0eba iTegno
+vendor WINMAXGROUP 0x0ed1 WinMaxGroup
+vendor TOD 0x0ede TOD
+vendor EGALAX 0x0eef eGalax, Inc.
+vendor AIRPRIME 0x0f3d AirPrime, Inc.
+vendor MICROTUNE 0x0f4d Microtune
+vendor VTECH 0x0f88 VTech
+vendor FALCOM 0x0f94 Falcom Wireless Communications GmbH
+vendor RIM 0x0fca Research In Motion
+vendor DYNASTREAM 0x0fcf Dynastream Innovations
+vendor QUALCOMM 0x1004 Qualcomm
+vendor DESKNOTE 0x1019 Desknote
+vendor GIGABYTE 0x1044 GIGABYTE
+vendor WESTERN 0x1058 Western Digital
+vendor MOTOROLA 0x1063 Motorola
+vendor CCYU 0x1065 CCYU Technology
+vendor CURITEL 0x106c Curitel Communications Inc
+vendor SILABS2 0x10a6 SILABS2
+vendor USI 0x10ab USI
+vendor PLX 0x10b5 PLX
+vendor ASANTE 0x10bd Asante
+vendor SILABS 0x10c4 Silicon Labs
+vendor ANALOG 0x1110 Analog Devices
+vendor TENX 0x1130 Ten X Technology, Inc.
+vendor ISSC 0x1131 Integrated System Solution Corp.
+vendor JRC 0x1145 Japan Radio Company
+vendor SPHAIRON 0x114b Sphairon Access Systems GmbH
+vendor DELORME 0x1163 DeLorme
+vendor SERVERWORKS 0x1166 ServerWorks
+vendor ACERCM 0x1189 Acer Communications & Multimedia
+vendor SIERRA 0x1199 Sierra Wireless
+vendor TOPFIELD 0x11db Topfield Co., Ltd
+vendor SIEMENS3 0x11f5 Siemens
+vendor PROLIFIC2 0x11f6 Prolific
+vendor ALCATEL 0x11f7 Alcatel
+vendor UNKNOWN3 0x1233 Unknown vendor
+vendor TSUNAMI 0x1241 Tsunami
+vendor PHEENET 0x124a Pheenet
+vendor TARGUS 0x1267 Targus
+vendor TWINMOS 0x126f TwinMOS
+vendor TENDA 0x1286 Tenda
+vendor CREATIVE2 0x1292 Creative Labs
+vendor BELKIN2 0x1293 Belkin Components
+vendor CYBERTAN 0x129b CyberTAN Technology
+vendor HUAWEI 0x12d1 Huawei Technologies
+vendor ARANEUS 0x12d8 Araneus Information Systems
+vendor TAPWAVE 0x12ef Tapwave
+vendor AINCOMM 0x12fd Aincomm
+vendor MOBILITY 0x1342 Mobility
+vendor DICKSMITH 0x1371 Dick Smith Electronics
+vendor NETGEAR3 0x1385 Netgear
+vendor BALTECH 0x13ad Baltech
+vendor CISCOLINKSYS 0x13b1 Cisco-Linksys
+vendor SHARK 0x13d2 Shark
+vendor NOVATEL 0x1410 Novatel Wireless
+vendor MERLIN 0x1416 Merlin
+vendor WISTRONNEWEB 0x1435 Wistron NeWeb
+vendor RADIOSHACK 0x1453 Radio Shack
+vendor HUAWEI3COM 0x1472 Huawei-3Com
+vendor SILICOM 0x1485 Silicom
+vendor RALINK 0x148f Ralink Technology
+vendor IMAGINATION 0x149a Imagination Technologies
+vendor CONCEPTRONIC2 0x14b2 Conceptronic
+vendor PLANEX3 0x14ea Planex Communications
+vendor SILICONPORTALS 0x1527 Silicon Portals
+vendor UBIQUAM 0x1529 UBIQUAM Co., Ltd.
+vendor UBLOX 0x1546 U-blox
+vendor PNY 0x154b PNY
+vendor OQO 0x1557 OQO
+vendor UMEDIA 0x157e U-MEDIA Communications
+vendor FIBERLINE 0x1582 Fiberline
+vendor SPARKLAN 0x15a9 SparkLAN
+vendor SOHOWARE 0x15e8 SOHOware
+vendor UMAX 0x1606 UMAX Data Systems
+vendor INSIDEOUT 0x1608 Inside Out Networks
+vendor GOODWAY 0x1631 Good Way Technology
+vendor ENTREGA 0x1645 Entrega
+vendor ACTIONTEC 0x1668 Actiontec Electronics
+vendor ATHEROS 0x168c Atheros Communications
+vendor GIGASET 0x1690 Gigaset
+vendor GLOBALSUN 0x16ab Global Sun Technology
+vendor ANYDATA 0x16d5 AnyDATA Corporation
+vendor JABLOTRON 0x16d6 Jablotron
+vendor CMOTECH 0x16d8 C-motech
+vendor AXESSTEL 0x1726 Axesstel Co., Ltd.
+vendor LINKSYS4 0x1737 Linksys
+vendor SENAO 0x1740 Senao
+vendor METAGEEK 0x1781 MetaGeek
+vendor AMIT 0x18c5 AMIT
+vendor QCOM 0x18e8 Qcom
+vendor LINKSYS3 0x1915 Linksys
+vendor QUALCOMMINC 0x19d2 Qualcomm, Incorporated
+vendor STELERA 0x1a8d Stelera Wireless
+vendor DRESDENELEKTRONIK 0x1cf1 dresden elektronik
+vendor DLINK 0x2001 D-Link
+vendor PLANEX2 0x2019 Planex Communications
+vendor ERICSSON 0x2282 Ericsson
+vendor MOTOROLA2 0x22b8 Motorola
+vendor TRIPPLITE 0x2478 Tripp-Lite
+vendor HIROSE 0x2631 Hirose Electric
+vendor NHJ 0x2770 NHJ
+vendor PLANEX 0x2c02 Planex Communications
+vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd
+vendor AEI 0x3334 AEI
+vendor HANK 0x3353 Hank Connection
+vendor PQI 0x3538 PQI
+vendor DAISY 0x3579 Daisy Technology
+vendor NI 0x3923 National Instruments
+vendor MICRONET 0x3980 Micronet Communications
+vendor IODATA2 0x40bb I-O Data
+vendor IRIVER 0x4102 iRiver
+vendor DELL 0x413c Dell
+vendor WCH 0x4348 QinHeng Electronics
+vendor ACEECA 0x4766 Aceeca
+vendor AVERATEC 0x50c2 Averatec
+vendor SWEEX 0x5173 Sweex
+vendor ONSPEC2 0x55aa OnSpec Electronic Inc.
+vendor ZINWELL 0x5a57 Zinwell
+vendor SITECOM 0x6189 Sitecom
+vendor ARKMICRO 0x6547 Arkmicro Technologies Inc.
+vendor 3COM2 0x6891 3Com
+vendor INTEL 0x8086 Intel
+vendor SITECOM2 0x9016 Sitecom
+vendor MOSCHIP 0x9710 MosChip Semiconductor
+vendor 3COM3 0xa727 3Com
+vendor HP2 0xf003 Hewlett Packard
+vendor USRP 0xfffe GNU Radio USRP
+
+/*
+ * List of known products. Grouped by vendor.
+ */
+
+/* 3Com products */
+product 3COM HOMECONN 0x009d HomeConnect Camera
+product 3COM 3CREB96 0x00a0 Bluetooth USB Adapter
+product 3COM 3C19250 0x03e8 3C19250 Ethernet Adapter
+product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless Adapter
+product 3COM 3C460 0x11f8 HomeConnect 3C460
+product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice FaxModem Pro
+product 3COM 3C460B 0x4601 HomeConnect 3C460B
+product 3COM2 3CRUSB10075 0xa727 3CRUSB10075
+product 3COM3 AR5523_1 0x6893 AR5523
+product 3COM3 AR5523_2 0x6895 AR5523
+product 3COM3 AR5523_3 0x6897 AR5523
+
+product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem
+product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA
+product 3COMUSR HOMECONN 0x009d 3Com HomeConnect Camera
+product 3COMUSR USR56K 0x3021 U.S. Robotics 56000 Voice FaxModem Pro
+
+/* AboCom products */
+product ABOCOM XX1 0x110c XX1
+product ABOCOM XX2 0x200c XX2
+product ABOCOM URE450 0x4000 URE450 Ethernet Adapter
+product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter
+product ABOCOM DSB650TX_PNA 0x4003 1/10/100 Ethernet Adapter
+product ABOCOM XX4 0x4004 XX4
+product ABOCOM XX5 0x4007 XX5
+product ABOCOM XX6 0x400b XX6
+product ABOCOM XX7 0x400c XX7
+product ABOCOM RTL8151 0x401a RTL8151
+product ABOCOM XX8 0x4102 XX8
+product ABOCOM XX9 0x4104 XX9
+product ABOCOM UF200 0x420a UF200 Ethernet
+product ABOCOM WL54 0x6001 WL54
+product ABOCOM XX10 0xabc1 XX10
+product ABOCOM BWU613 0xb000 BWU613
+product ABOCOM HWU54DM 0xb21b HWU54DM
+product ABOCOM RT2573_2 0xb21c RT2573
+product ABOCOM RT2573_3 0xb21d RT2573
+product ABOCOM RT2573_4 0xb21e RT2573
+product ABOCOM WUG2700 0xb21f WUG2700
+
+/* Accton products */
+product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter
+product ACCTON 2664W 0x3501 2664W
+product ACCTON 111 0x3503 T-Sinus 111 Wireless Adapter
+product ACCTON SMCWUSBG 0x4505 SMCWUSB-G
+product ACCTON PRISM_GT 0x4521 PrismGT USB 2.0 WLAN
+product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter
+product ACCTON ZD1211B 0xe501 ZD1211B
+
+/* Aceeca products */
+product ACEECA MEZ1000 0x0001 MEZ1000 RDA
+
+/* Acer Communications & Multimedia (oemd by Surecom) */
+product ACERCM EP1427X2 0x0893 EP-1427X-2 Ethernet Adapter
+
+/* Acer Labs products */
+product ACERLABS M5632 0x5632 USB 2.0 Data Link
+
+/* Acer Peripherals, Inc. products */
+product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U
+product ACERP ACERSCAN_320U 0x2022 Acerscan 320U
+product ACERP ACERSCAN_640U 0x2040 Acerscan 640U
+product ACERP ACERSCAN_620U 0x2060 Acerscan 620U
+product ACERP ACERSCAN_4300U 0x20b0 Benq 3300U/4300U
+product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT
+product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U
+product ACERP ATAPI 0x6003 ATA/ATAPI Adapter
+product ACERP AWL300 0x9000 AWL300 Wireless Adapter
+product ACERP AWL400 0x9001 AWL400 Wireless Adapter
+
+/* Acer Warp products */
+product ACERW WARPLINK 0x0204 Warplink
+
+/* Actiontec, Inc. products */
+product ACTIONTEC PRISM_25 0x0408 Prism2.5 Wireless Adapter
+product ACTIONTEC PRISM_25A 0x0421 Prism2.5 Wireless Adapter A
+product ACTIONTEC FREELAN 0x6106 ROPEX FreeLan 802.11b
+product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet Adapter
+
+/* ACTiSYS products */
+product ACTISYS IR2000U 0x0011 ACT-IR2000U FIR
+
+/* ActiveWire, Inc. products */
+product ACTIVEWIRE IOBOARD 0x0100 I/O Board
+product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware
+
+/* Adaptec products */
+product ADAPTEC AWN8020 0x0020 AWN-8020 WLAN
+
+/* Addtron products */
+product ADDTRON AWU120 0xff31 AWU-120
+
+/* ADMtek products */
+product ADMTEK PEGASUSII_4 0x07c2 AN986A Ethernet
+product ADMTEK PEGASUS 0x0986 AN986 Ethernet
+product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet
+product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet
+product ADMTEK PEGASUSII_3 0x8515 AN8515 Ethernet
+
+/* ADDON products */
+/* PNY OEMs these */
+product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive
+product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive
+product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive
+product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive)
+
+/* Addonics products */
+product ADDONICS2 CABLE_205 0xa001 Cable 205
+
+/* ADS products */
+product ADS UBS10BT 0x0008 UBS-10BT Ethernet
+product ADS UBS10BTX 0x0009 UBS-10BT Ethernet
+
+/* AEI products */
+product AEI FASTETHERNET 0x1701 Fast Ethernet
+
+/* Agate Technologies products */
+product AGATE QDRIVE 0x0378 Q-Drive
+
+/* AGFA products */
+product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U
+product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U
+product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch
+product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U
+product AGFA SNAPSCANE40 0x208d SnapScan e40
+product AGFA SNAPSCANE50 0x208f SnapScan e50
+product AGFA SNAPSCANE20 0x2091 SnapScan e20
+product AGFA SNAPSCANE25 0x2095 SnapScan e25
+product AGFA SNAPSCANE26 0x2097 SnapScan e26
+product AGFA SNAPSCANE52 0x20fd SnapScan e52
+
+/* Ain Communication Technology products */
+product AINCOMM AWU2000B 0x1001 AWU2000B Wireless Adapter
+
+/* AIPTEK products */
+product AIPTEK POCKETCAM3M 0x2011 PocketCAM 3Mega
+product AIPTEK2 PENCAM_MEGA_1_3 0x504a PenCam Mega 1.3
+
+/* AirPrime products */
+product AIRPRIME PC5220 0x0112 CDMA Wireless PC Card
+
+/* AKS products */
+product AKS USBHASP 0x0001 USB-HASP 0.06
+
+/* Alcor Micro, Inc. products */
+product ALCOR2 KBD_HUB 0x2802 Kbd Hub
+
+product ALCOR TRANSCEND 0x6387 Transcend JetFlash Drive
+product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub
+product ALCOR AU9814 0x9215 AU9814 Hub
+product ALCOR UMCR_9361 0x9361 USB Multimedia Card Reader
+product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard
+product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub
+
+/* Altec Lansing products */
+product ALTEC ADA70 0x0070 ADA70 Speakers
+product ALTEC ASC495 0xff05 ASC495 Speakers
+
+/* Allied Telesyn International products */
+product ALLIEDTELESYN ATUSB100 0xb100 AT-USB100
+
+/* American Power Conversion products */
+product APC UPS 0x0002 Uninterruptible Power Supply
+
+/* Ambit Microsystems products */
+product AMBIT WLAN 0x0302 WLAN
+product AMBIT NTL_250 0x6098 NTL 250 cable modem
+
+/* AMIT products */
+product AMIT CGWLUSB2GO 0x0002 CG-WLUSB2GO
+
+/* Anchor products */
+product ANCHOR EZUSB 0x2131 EZUSB
+product ANCHOR EZLINK 0x2720 EZLINK
+
+/* AnyData products */
+product ANYDATA ADU_E100X 0x6501 CDMA 2000 1xRTT/EV-DO USB Modem
+product ANYDATA ADU_500A 0x6502 CDMA 2000 EV-DO USB Modem
+
+/* AOX, Inc. products */
+product AOX USB101 0x0008 Ethernet
+
+/* American Power Conversion products */
+product APC UPS 0x0002 Uninterruptible Power Supply
+
+/* Apple Computer products */
+product APPLE EXT_KBD 0x020c Apple Extended USB Keyboard
+product APPLE OPTMOUSE 0x0302 Optical mouse
+product APPLE MIGHTYMOUSE 0x0304 Mighty Mouse
+product APPLE EXT_KBD_HUB 0x1003 Hub in Apple Extended USB Keyboard
+product APPLE SPEAKERS 0x1101 Speakers
+product APPLE IPOD 0x1201 iPod
+product APPLE IPOD2G 0x1202 iPod 2G
+product APPLE IPOD3G 0x1203 iPod 3G
+product APPLE IPOD_04 0x1204 iPod '04'
+product APPLE IPODMINI 0x1205 iPod Mini
+product APPLE IPOD_06 0x1206 iPod '06'
+product APPLE IPOD_07 0x1207 iPod '07'
+product APPLE IPOD_08 0x1208 iPod '08'
+product APPLE IPODVIDEO 0x1209 iPod Video
+product APPLE IPODNANO 0x120a iPod Nano
+product APPLE IPHONE 0x1290 iPhone
+product APPLE IPHONE_3G 0x1292 iPhone 3G
+product APPLE ETHERNET 0x1402 Ethernet A1277
+
+/* Arkmicro Technologies */
+product ARKMICRO ARK3116 0x0232 ARK3116 Serial
+
+/* Asahi Optical products */
+product ASAHIOPTICAL OPTIO230 0x0004 Digital camera
+product ASAHIOPTICAL OPTIO330 0x0006 Digital camera
+
+/* Asante products */
+product ASANTE EA 0x1427 Ethernet
+
+/* ASIX Electronics products */
+product ASIX AX88172 0x1720 10/100 Ethernet
+product ASIX AX88178 0x1780 AX88178
+product ASIX AX88772 0x7720 AX88772
+
+/* ASUS products */
+product ASUS WL167G 0x1707 WL-167g Wireless Adapter
+product ASUS WL159G 0x170c WL-159g
+product ASUS A9T_WIFI 0x171b A9T wireless
+product ASUS RT2573_1 0x1723 RT2573
+product ASUS RT2573_2 0x1724 RT2573
+product ASUS LCM 0x1726 LCM display
+product ASUS P535 0x420f ASUS P535 PDA
+
+/* ATen products */
+product ATEN UC1284 0x2001 Parallel printer
+product ATEN UC10T 0x2002 10Mbps Ethernet
+product ATEN UC110T 0x2007 UC-110T Ethernet
+product ATEN UC232A 0x2008 Serial
+product ATEN UC210T 0x2009 UC-210T Ethernet
+product ATEN DSB650C 0x4000 DSB-650C
+
+/* Atheros Communications products */
+product ATHEROS AR5523 0x0001 AR5523
+product ATHEROS AR5523_NF 0x0002 AR5523 (no firmware)
+product ATHEROS2 AR5523_1 0x0001 AR5523
+product ATHEROS2 AR5523_1_NF 0x0002 AR5523 (no firmware)
+product ATHEROS2 AR5523_2 0x0003 AR5523
+product ATHEROS2 AR5523_2_NF 0x0004 AR5523 (no firmware)
+product ATHEROS2 AR5523_3 0x0005 AR5523
+product ATHEROS2 AR5523_3_NF 0x0006 AR5523 (no firmware)
+
+/* Atmel Comp. products */
+product ATMEL UHB124 0x3301 UHB124 hub
+product ATMEL DWL120 0x7603 DWL-120 Wireless Adapter
+product ATMEL BW002 0x7605 BW002 Wireless Adapter
+product ATMEL WL1130USB 0x7613 WL-1130 USB
+product ATMEL AT76C505A 0x7614 AT76c505a Wireless Adapter
+
+/* Avision products */
+product AVISION 1200U 0x0268 1200U scanner
+
+/* Axesstel products */
+product AXESSTEL DATAMODEM 0x1000 Data Modem
+
+/* Baltech products */
+product BALTECH CARDREADER 0x9999 Card reader
+
+/* B&B Electronics products */
+product BBELECTRONICS USOTL4 0xAC01 RS-422/485
+
+/* Belkin products */
+/*product BELKIN F5U111 0x???? F5U111 Ethernet*/
+product BELKIN F5D6050 0x0050 F5D6050 802.11b Wireless Adapter
+product BELKIN FBT001V 0x0081 FBT001v2 Bluetooth
+product BELKIN FBT003V 0x0084 FBT003v2 Bluetooth
+product BELKIN F5U103 0x0103 F5U103 Serial
+product BELKIN F5U109 0x0109 F5U109 Serial
+product BELKIN USB2SCSI 0x0115 USB to SCSI
+product BELKIN F8T012 0x0121 F8T012xx1 Bluetooth USB Adapter
+product BELKIN USB2LAN 0x0121 USB to LAN
+product BELKIN F5U208 0x0208 F5U208 VideoBus II
+product BELKIN F5U237 0x0237 F5U237 USB 2.0 7-Port Hub
+product BELKIN F5U257 0x0257 F5U257 Serial
+product BELKIN F5U409 0x0409 F5U409 Serial
+product BELKIN F6C550AVR 0x0551 F6C550-AVR UPS
+product BELKIN F5U120 0x1203 F5U120-PC Hub
+product BELKIN ZD1211B 0x4050 ZD1211B
+product BELKIN F5D5055 0x5055 F5D5055
+product BELKIN F5D7050 0x7050 F5D7050 Wireless Adapter
+product BELKIN F5D7051 0x7051 F5D7051 54g USB Network Adapter
+product BELKIN F5D7050A 0x705a F5D7050A Wireless Adapter
+/* Also sold as 'Ativa 802.11g wireless card' */
+product BELKIN F5D7050_V4000 0x705c F5D7050 v4000 Wireless Adapter
+product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter
+product BELKIN2 F5U002 0x0002 F5U002 Parallel printer
+
+/* Billionton products */
+product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet
+product BILLIONTON USBLP100 0x0987 USB100LP
+product BILLIONTON USBEL100 0x0988 USB100EL
+product BILLIONTON USBE100 0x8511 USBE100
+product BILLIONTON USB2AR 0x90ff USB2AR Ethernet
+
+/* Broadcom products */
+product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle
+
+/* Brother Industries products */
+product BROTHER HL1050 0x0002 HL-1050 laser printer
+
+/* Behavior Technology Computer products */
+product BTC BTC7932 0x6782 Keyboard with mouse port
+
+/* Canon, Inc. products */
+product CANON N656U 0x2206 CanoScan N656U
+product CANON N1220U 0x2207 CanoScan N1220U
+product CANON D660U 0x2208 CanoScan D660U
+product CANON N676U 0x220d CanoScan N676U
+product CANON N1240U 0x220e CanoScan N1240U
+product CANON LIDE25 0x2220 CanoScan LIDE 25
+product CANON S10 0x3041 PowerShot S10
+product CANON S100 0x3045 PowerShot S100
+product CANON S200 0x3065 PowerShot S200
+product CANON REBELXT 0x30ef Digital Rebel XT
+
+/* CATC products */
+product CATC NETMATE 0x000a Netmate Ethernet
+product CATC NETMATE2 0x000c Netmate2 Ethernet
+product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer
+product CATC ANDROMEDA 0x1237 Andromeda hub
+
+/* CASIO products */
+product CASIO QV_DIGICAM 0x1001 QV DigiCam
+product CASIO EXS880 0x1105 Exilim EX-S880
+product CASIO BE300 0x2002 BE-300 PDA
+product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB
+
+/* CCYU products */
+product CCYU ED1064 0x2136 EasyDisk ED1064
+
+/* Century products */
+product CENTURY EX35QUAT 0x011e Century USB Disk Enclosure
+
+/* Cherry products */
+product CHERRY MY3000KBD 0x0001 My3000 keyboard
+product CHERRY MY3000HUB 0x0003 My3000 hub
+product CHERRY CYBOARD 0x0004 CyBoard Keyboard
+
+/* Chic Technology products */
+product CHIC MOUSE1 0x0001 mouse
+product CHIC CYPRESS 0x0003 Cypress USB Mouse
+
+/* Chicony products */
+product CHICONY KB8933 0x0001 KB-8933 keyboard
+product CHICONY2 TWINKLECAM 0x600d TwinkleCam USB camera
+
+/* CH Products */
+product CHPRODUCTS PROTHROTTLE 0x00f1 Pro Throttle
+product CHPRODUCTS PROPEDALS 0x00f2 Pro Pedals
+product CHPRODUCTS FIGHTERSTICK 0x00f3 Fighterstick
+product CHPRODUCTS FLIGHTYOKE 0x00ff Flight Sim Yoke
+
+/* Cisco-Linksys products */
+product CISCOLINKSYS WUSB54G 0x000d WUSB54G Wireless Adapter
+product CISCOLINKSYS WUSB54GP 0x0011 WUSB54GP Wireless Adapter
+product CISCOLINKSYS USB200MV2 0x0018 USB200M v2
+product CISCOLINKSYS HU200TS 0x001a HU200TS Wireless Adapter
+product CISCOLINKSYS WUSB54GC 0x0020 WUSB54GC
+product CISCOLINKSYS WUSB54GR 0x0023 WUSB54GR
+product CISCOLINKSYS WUSBF54G 0x0024 WUSBF54G
+
+/* CMOTECH products */
+product CMOTECH CNU510 0x5141 CDMA Technologies USB modem
+product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem
+product CMOTECH CGU628 0x6006 CGU-628
+product CMOTECH CDMA_MODEM1 0x6280 CDMA Technologies USB modem
+product CMOTECH DISK 0xf000 disk mode
+
+/* Compaq products */
+product COMPAQ IPAQPOCKETPC 0x0003 iPAQ PocketPC
+product COMPAQ PJB100 0x504a Personal Jukebox PJB100
+product COMPAQ IPAQLINUX 0x505a iPAQ Linux
+
+/* Composite Corp products looks the same as "TANGTOP" */
+product COMPOSITE USBPS2 0x0001 USB to PS2 Adaptor
+
+/* Conceptronic products */
+product CONCEPTRONIC PRISM_GT 0x3762 PrismGT USB 2.0 WLAN
+product CONCEPTRONIC C11U 0x7100 C11U
+product CONCEPTRONIC WL210 0x7110 WL-210
+product CONCEPTRONIC AR5523_1 0x7801 AR5523
+product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware)
+product CONCEPTRONIC AR5523_2 0x7811 AR5523
+product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware)
+product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN
+product CONCEPTRONIC2 C54RU2 0x3c22 C54RU
+
+/* Connectix products */
+product CONNECTIX QUICKCAM 0x0001 QuickCam
+
+/* Corega products */
+product COREGA ETHER_USB_T 0x0001 Ether USB-T
+product COREGA FETHER_USB_TX 0x0004 FEther USB-TX
+product COREGA WLAN_USB_USB_11 0x000c WirelessLAN USB-11
+product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS
+product COREGA WLANUSB 0x0012 Wireless LAN Stick-11
+product COREGA FETHER_USB2_TX 0x0017 FEther USB2-TX
+product COREGA WLUSB_11_KEY 0x001a ULUSB-11 Key
+product COREGA CGWLUSB2GL 0x002d CG-WLUSB2GL
+product COREGA CGWLUSB2GPX 0x002e CG-WLUSB2GPX
+product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11
+product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC
+
+/* Creative products */
+product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player
+product CREATIVE NOMAD_IIMG 0x4004 Nomad II MG
+product CREATIVE NOMAD 0x4106 Nomad
+product CREATIVE2 VOIP_BLASTER 0x0258 Voip Blaster
+product CREATIVE3 OPTICAL_MOUSE 0x0001 Notebook Optical Mouse
+
+/* Cambridge Silicon Radio Ltd. products */
+product CSR BT_DONGLE 0x0001 Bluetooth USB dongle
+product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State
+
+/* CTX products */
+product CTX EX1300 0x9999 Ex1300 hub
+
+/* Curitel products */
+product CURITEL HX550C 0x1101 CDMA 2000 1xRTT USB modem (HX-550C)
+product CURITEL HX57XB 0x2101 CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600)
+product CURITEL PC5740 0x3701 Broadband Wireless modem
+
+/* CyberPower products */
+product CYBERPOWER 1500CAVRLCD 0x0501 1500CAVRLCD
+
+/* CyberTAN Technology products */
+product CYBERTAN TG54USB 0x1666 TG54USB
+
+/* Cypress Semiconductor products */
+product CYPRESS MOUSE 0x0001 mouse
+product CYPRESS THERMO 0x0002 thermometer
+product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy
+product CYPRESS KBDHUB 0x0101 Keyboard/Hub
+product CYPRESS FMRADIO 0x1002 FM Radio
+product CYPRESS USBRS232 0x5500 USB-RS232 Interface
+product CYPRESS SLIM_HUB 0x6560 Slim Hub
+
+/* Daisy Technology products */
+product DAISY DMC 0x6901 USB MultiMedia Reader
+
+/* Dallas Semiconductor products */
+product DALLAS J6502 0x4201 J-6502 speakers
+
+/* Dell products */
+product DELL PORT 0x0058 Port Replicator
+product DELL AIO926 0x5115 Photo AIO Printer 926
+product DELL BC02 0x8000 BC02 Bluetooth USB Adapter
+product DELL PRISM_GT_1 0x8102 PrismGT USB 2.0 WLAN
+product DELL TM350 0x8103 TrueMobile 350 Bluetooth USB Adapter
+product DELL PRISM_GT_2 0x8104 PrismGT USB 2.0 WLAN
+product DELL U740 0x8135 Dell U740 CDMA
+
+/* Delorme Paublishing products */
+product DELORME EARTHMATE 0x0100 Earthmate GPS
+
+/* Desknote products */
+product DESKNOTE UCR_61S2B 0x0c55 UCR-61S2B
+
+/* Diamond products */
+product DIAMOND RIO500USB 0x0001 Rio 500 USB
+
+/* Dick Smith Electronics (really C-Net) products */
+product DICKSMITH RT2573 0x9022 RT2573
+product DICKSMITH CWD854F 0x9032 C-Net CWD-854 rev F
+
+/* Digi International products */
+product DIGI ACCELEPORT2 0x0002 AccelePort USB 2
+product DIGI ACCELEPORT4 0x0004 AccelePort USB 4
+product DIGI ACCELEPORT8 0x0008 AccelePort USB 8
+
+/* D-Link products */
+/*product DLINK DSBS25 0x0100 DSB-S25 serial*/
+product DLINK DUBE100 0x1a00 10/100 Ethernet
+product DLINK DSB650TX4 0x200c 10/100 Ethernet
+product DLINK DWL120E 0x3200 DWL-120 rev E
+product DLINK DWL122 0x3700 DWL-122
+product DLINK DWLG120 0x3701 DWL-G120
+product DLINK DWL120F 0x3702 DWL-120 rev F
+product DLINK DWLAG132 0x3a00 DWL-AG132
+product DLINK DWLAG132_NF 0x3a01 DWL-AG132 (no firmware)
+product DLINK DWLG132 0x3a02 DWL-G132
+product DLINK DWLG132_NF 0x3a03 DWL-G132 (no firmware)
+product DLINK DWLAG122 0x3a04 DWL-AG122
+product DLINK DWLAG122_NF 0x3a05 DWL-AG122 (no firmware)
+product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter
+product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1
+product DLINK DSB650C 0x4000 10Mbps Ethernet
+product DLINK DSB650TX1 0x4001 10/100 Ethernet
+product DLINK DSB650TX 0x4002 10/100 Ethernet
+product DLINK DSB650TX_PNA 0x4003 1/10/100 Ethernet
+product DLINK DSB650TX3 0x400b 10/100 Ethernet
+product DLINK DSB650TX2 0x4102 10/100 Ethernet
+product DLINK DSB650 0xabc1 10/100 Ethernet
+product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1
+product DLINK2 WUA1340 0x3c04 WUA-1340
+product DLINK2 DWA111 0x3c06 DWA-111
+product DLINK2 DWA110 0x3c07 DWA-110
+
+/* DMI products */
+product DMI CFSM_RW 0xa109 CF/SM Reader/Writer
+
+/* DrayTek products */
+product DRAYTEK VIGOR550 0x0550 Vigor550
+
+/* dresden elektronik products */
+product DRESDENELEKTRONIK SENSORTERMINALBOARD 0x0001 SensorTerminalBoard
+
+/* Dynastream Innovations */
+product DYNASTREAM ANTDEVBOARD 0x1003 ANT dev board
+
+/* EIZO products */
+product EIZO HUB 0x0000 hub
+product EIZO MONITOR 0x0001 monitor
+
+/* ELCON Systemtechnik products */
+product ELCON PLAN 0x0002 Goldpfeil P-LAN
+
+/* Elecom products */
+product ELECOM MOUSE29UO 0x0002 mouse 29UO
+product ELECOM LDUSBTX0 0x200c LD-USB/TX
+product ELECOM LDUSBTX1 0x4002 LD-USB/TX
+product ELECOM LDUSBLTX 0x4005 LD-USBL/TX
+product ELECOM LDUSBTX2 0x400b LD-USB/TX
+product ELECOM LDUSB20 0x4010 LD-USB20
+product ELECOM UCSGT 0x5003 UC-SGT
+product ELECOM UCSGT0 0x5004 UC-SGT
+product ELECOM LDUSBTX3 0xabc1 LD-USB/TX
+
+/* Elsa products */
+product ELSA MODEM1 0x2265 ELSA Modem Board
+product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet
+
+/* EMS products */
+product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter
+
+/* Entrega products */
+product ENTREGA 1S 0x0001 1S serial
+product ENTREGA 2S 0x0002 2S serial
+product ENTREGA 1S25 0x0003 1S25 serial
+product ENTREGA 4S 0x0004 4S serial
+product ENTREGA E45 0x0005 E45 Ethernet
+product ENTREGA CENTRONICS 0x0006 Parallel Port
+product ENTREGA XX1 0x0008 Ethernet
+product ENTREGA 1S9 0x0093 1S9 serial
+product ENTREGA EZUSB 0x8000 EZ-USB
+/*product ENTREGA SERIAL 0x8001 DB25 Serial*/
+product ENTREGA 2U4S 0x8004 2U4S serial/usb hub
+product ENTREGA XX2 0x8005 Ethernet
+/*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/
+
+/* Epson products */
+product EPSON PRINTER1 0x0001 USB Printer
+product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac
+product EPSON PRINTER3 0x0003 ISD USB Smart Cable
+product EPSON PRINTER5 0x0005 USB Printer
+product EPSON 636 0x0101 Perfection 636U / 636Photo scanner
+product EPSON 610 0x0103 Perfection 610 scanner
+product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner
+product EPSON 1600 0x0107 Expression 1600 scanner
+product EPSON 1640 0x010a Perfection 1640SU scanner
+product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner
+product EPSON 640U 0x010c Perfection 640U scanner
+product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner
+product EPSON 1650 0x0110 Perfection 1650 scanner
+product EPSON GT9700F 0x0112 GT-9700F scanner
+product EPSON GT9300UF 0x011b GT-9300UF scanner
+product EPSON 3200 0x011c Perfection 3200 scanner
+product EPSON 1260 0x011d Perfection 1260 scanner
+product EPSON 1660 0x011e Perfection 1660 scanner
+product EPSON 1670 0x011f Perfection 1670 scanner
+product EPSON 1270 0x0120 Perfection 1270 scanner
+product EPSON 2480 0x0121 Perfection 2480 scanner
+product EPSON 3590 0x0122 Perfection 3590 scanner
+product EPSON 4990 0x012a Perfection 4990 Photo scanner
+product EPSON STYLUS_875DC 0x0601 Stylus Photo 875DC Card Reader
+product EPSON STYLUS_895 0x0602 Stylus Photo 895 Card Reader
+product EPSON CX5400 0x0808 CX5400 scanner
+product EPSON 3500 0x080e CX-3500/3600/3650 MFP
+product EPSON RX425 0x080f Stylus Photo RX425 scanner
+product EPSON DX3800 0x0818 CX3700/CX3800/DX38x0 MFP scanner
+product EPSON 4800 0x0819 CX4700/CX4800/DX48x0 MFP scanner
+product EPSON 4200 0x0820 CX4100/CX4200/DX4200 MFP scanner
+product EPSON 5000 0x082b CX4900/CX5000/DX50x0 MFP scanner
+product EPSON 6000 0x082e CX5900/CX6000/DX60x0 MFP scanner
+product EPSON DX4000 0x082f DX4000 MFP scanner
+product EPSON DX7400 0x0838 CX7300/CX7400/DX7400 MFP scanner
+product EPSON DX8400 0x0839 CX8300/CX8400/DX8400 MFP scanner
+product EPSON SX100 0x0841 SX100/NX100 MFP scanner
+product EPSON NX300 0x0848 NX300 MFP scanner
+product EPSON SX200 0x0849 SX200/SX205 MFP scanner
+product EPSON SX400 0x084a SX400/NX400/TX400 MFP scanner
+
+/* e-TEK Labs products */
+product ETEK 1COM 0x8007 Serial
+
+/* Extended Systems products */
+product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA
+
+/* FEIYA products */
+product FEIYA 5IN1 0x1132 5-in-1 Card Reader
+
+/* Fiberline */
+product FIBERLINE WL430U 0x6003 WL-430U
+
+/* Fossil, Inc products */
+product FOSSIL WRISTPDA 0x0002 Wrist PDA
+
+/* Freecom products */
+product FREECOM DVD 0xfc01 DVD drive
+
+/* Fujitsu Siemens Computers products */
+product FSC E5400 0x1009 PrismGT USB 2.0 WLAN
+
+/* Future Technology Devices products */
+product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial
+product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial
+product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial
+/* Gude Analog- und Digitalsysteme products also uses FTDI's id: */
+product FTDI TACTRIX_OPENPORT_13M 0xcc48 OpenPort 1.3 Mitsubishi
+product FTDI TACTRIX_OPENPORT_13S 0xcc49 OpenPort 1.3 Subaru
+product FTDI TACTRIX_OPENPORT_13U 0xcc4a OpenPort 1.3 Universal
+product FTDI EISCOU 0xe888 Expert ISDN Control USB
+product FTDI UOPTBR 0xe889 USB-RS232 OptoBridge
+product FTDI EMCU2D 0xe88a Expert mouseCLOCK USB II
+product FTDI PCMSFU 0xe88b Precision Clock MSF USB
+product FTDI EMCU2H 0xe88c Expert mouseCLOCK USB II HBG
+product FTDI MAXSTREAM 0xee18 Maxstream PKG-U
+product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial
+product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3
+product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5
+product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family
+product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family
+product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD
+product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD
+product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD
+product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD
+product FTDI CFA_635 0xfc0d Crystalfontz CFA-635 USB LCD
+product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation
+
+/* Fuji photo products */
+product FUJIPHOTO MASS0100 0x0100 Mass Storage
+
+/* Fujitsu protducts */
+product FUJITSU AH_F401U 0x105b AH-F401U Air H device
+
+/* Garmin products */
+product GARMIN IQUE_3600 0x0004 iQue 3600
+
+/* General Instruments (Motorola) products */
+product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem
+
+/* Genesys Logic products */
+product GENESYS GL620USB 0x0501 GL620USB Host-Host interface
+product GENESYS GL650 0x0604 GL650 Hub
+product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader
+product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2
+product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge
+product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader
+
+/* GIGABYTE products */
+product GIGABYTE GN54G 0x8001 GN-54G
+product GIGABYTE GNBR402W 0x8002 GN-BR402W
+product GIGABYTE GNWLBM101 0x8003 GN-WLBM101
+product GIGABYTE GNWBKG 0x8007 GN-WBKG
+product GIGABYTE GNWB01GS 0x8008 GN-WB01GS
+product GIGABYTE GNWI05GS 0x800a GN-WI05GS
+
+/* Gigaset products */
+product GIGASET WLAN 0x0701 WLAN
+product GIGASET SMCWUSBTG 0x0710 SMCWUSBT-G
+product GIGASET SMCWUSBTG_NF 0x0711 SMCWUSBT-G (no firmware)
+product GIGASET AR5523 0x0712 AR5523
+product GIGASET AR5523_NF 0x0713 AR5523 (no firmware)
+product GIGASET RT2573 0x0722 RT2573
+
+/* Global Sun Technology product */
+product GLOBALSUN AR5523_1 0x7801 AR5523
+product GLOBALSUN AR5523_1_NF 0x7802 AR5523 (no firmware)
+product GLOBALSUN AR5523_2 0x7811 AR5523
+product GLOBALSUN AR5523_2_NF 0x7812 AR5523 (no firmware)
+
+/* Globespan products */
+product GLOBESPAN PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN
+product GLOBESPAN PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN
+
+/* G.Mate, Inc products */
+product GMATE YP3X00 0x1001 YP3X00 PDA
+
+/* GoHubs products */
+product GOHUBS GOCOM232 0x1001 GoCOM232 Serial
+
+/* Good Way Technology products */
+product GOODWAY GWUSB2E 0x6200 GWUSB2E
+product GOODWAY RT2573 0xc019 RT2573
+
+/* Gravis products */
+product GRAVIS GAMEPADPRO 0x4001 GamePad Pro
+
+/* GREENHOUSE products */
+product GREENHOUSE KANA21 0x0001 CF-writer with MP3
+
+/* Griffin Technology */
+product GRIFFIN IMATE 0x0405 iMate, ADB Adapter
+
+/* Guillemot Corporation */
+product GUILLEMOT DALEADER 0xa300 DA Leader
+product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN
+product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB
+product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP
+
+/* Hagiwara products */
+product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader
+product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader
+product HAGIWARA FG 0x0005 FlashGate
+
+/* HAL Corporation products */
+product HAL IMR001 0x0011 Crossam2+USB IR commander
+
+/* Handspring, Inc. */
+product HANDSPRING VISOR 0x0100 Handspring Visor
+product HANDSPRING TREO 0x0200 Handspring Treo
+product HANDSPRING TREO600 0x0300 Handspring Treo 600
+
+/* Hauppauge Computer Works */
+product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM
+
+/* Hawking Technologies products */
+product HAWKING UF100 0x400c 10/100 USB Ethernet
+
+/* Hitachi, Ltd. products */
+product HITACHI DVDCAM_DZ_MV100A 0x0004 DVD-CAM DZ-MV100A Camcorder
+product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface
+
+/* HP products */
+product HP 895C 0x0004 DeskJet 895C
+product HP 4100C 0x0101 Scanjet 4100C
+product HP S20 0x0102 Photosmart S20
+product HP 880C 0x0104 DeskJet 880C
+product HP 4200C 0x0105 ScanJet 4200C
+product HP CDWRITERPLUS 0x0107 CD-Writer Plus
+product HP KBDHUB 0x010c Multimedia Keyboard Hub
+product HP G55XI 0x0111 OfficeJet G55xi
+product HP HN210W 0x011c HN210W 802.11b WLAN
+product HP 49GPLUS 0x0121 49g+ graphing calculator
+product HP 6200C 0x0201 ScanJet 6200C
+product HP S20b 0x0202 PhotoSmart S20
+product HP 815C 0x0204 DeskJet 815C
+product HP 3300C 0x0205 ScanJet 3300C
+product HP CDW8200 0x0207 CD-Writer Plus 8200e
+product HP MMKEYB 0x020c Multimedia keyboard
+product HP 1220C 0x0212 DeskJet 1220C
+product HP 810C 0x0304 DeskJet 810C/812C
+product HP 4300C 0x0305 Scanjet 4300C
+product HP CDW4E 0x0307 CD-Writer+ CD-4e
+product HP G85XI 0x0311 OfficeJet G85xi
+product HP 1200 0x0317 LaserJet 1200
+product HP 5200C 0x0401 Scanjet 5200C
+product HP 830C 0x0404 DeskJet 830C
+product HP 3400CSE 0x0405 ScanJet 3400cse
+product HP 6300C 0x0601 Scanjet 6300C
+product HP 840C 0x0604 DeskJet 840c
+product HP 2200C 0x0605 ScanJet 2200C
+product HP 5300C 0x0701 Scanjet 5300C
+product HP 4400C 0x0705 Scanjet 4400C
+product HP 4470C 0x0805 Scanjet 4470C
+product HP 82x0C 0x0b01 Scanjet 82x0C
+product HP 2300D 0x0b17 Laserjet 2300d
+product HP 970CSE 0x1004 Deskjet 970Cse
+product HP 5400C 0x1005 Scanjet 5400C
+product HP 2215 0x1016 iPAQ 22xx/Jornada 548
+product HP 568J 0x1116 Jornada 568
+product HP 930C 0x1204 DeskJet 930c
+product HP P2000U 0x1801 Inkjet P-2000U
+product HP 640C 0x2004 DeskJet 640c
+product HP 4670V 0x3005 ScanJet 4670v
+product HP P1100 0x3102 Photosmart P1100
+product HP OJ4215 0x3d11 OfficeJet 4215
+product HP HN210E 0x811c Ethernet HN210E
+product HP2 C500 0x6002 PhotoSmart C500
+product HP HS2300 0x1e1d hs2300 HSDPA (aka MC8775)
+
+/* HTC products */
+product HTC WINMOBILE 0x00ce HTC USB Sync
+product HTC PPC6700MODEM 0x00cf PPC6700 Modem
+product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync
+
+/* HUAWEI products */
+product HUAWEI MOBILE 0x1001 Huawei Mobile
+product HUAWEI E220 0x1003 Huawei HSDPA modem
+
+/* HUAWEI 3com products */
+product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g
+
+/* IBM Corporation */
+product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive
+
+/* Imagination Technologies products */
+product IMAGINATION DBX1 0x2107 DBX1 DSP core
+
+/* Inside Out Networks products */
+product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports
+
+/* In-System products */
+product INSYSTEM F5U002 0x0002 Parallel printer
+product INSYSTEM ATAPI 0x0031 ATAPI Adapter
+product INSYSTEM ISD110 0x0200 IDE Adapter ISD110
+product INSYSTEM ISD105 0x0202 IDE Adapter ISD105
+product INSYSTEM USBCABLE 0x081a USB cable
+product INSYSTEM STORAGE_V2 0x5701 USB Storage Adapter V2
+
+/* Intel products */
+product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera
+product INTEL TESTBOARD 0x9890 82930 test board
+
+/* Intersil products */
+product INTERSIL PRISM_GT 0x1000 PrismGT USB 2.0 WLAN
+product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN
+
+/* Interpid Control Systems products */
+product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface
+product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface
+
+/* I/O DATA products */
+product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2
+product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8
+product IODATA USBSSMRW 0x0314 USB-SSMRW SD-card
+product IODATA USBSDRW 0x031e USB-SDRW SD-card
+product IODATA USBETT 0x0901 USB ETT
+product IODATA USBETTX 0x0904 USB ETTX
+product IODATA USBETTXS 0x0913 USB ETTX
+product IODATA USBWNB11A 0x0919 USB WN-B11
+product IODATA USBWNB11 0x0922 USB Airport WN-B11
+product IODATA ETGUS2 0x0930 ETG-US2
+product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1
+product IODATA2 USB2SC 0x0a09 USB2.0-SCSI Bridge USB2-SC
+
+/* Iomega products */
+product IOMEGA ZIP100 0x0001 Zip 100
+product IOMEGA ZIP250 0x0030 Zip 250
+
+/* Ituner networks products */
+product ITUNERNET USBLCD2X20 0x0002 USB-LCD 2x20
+product ITUNERNET USBLCD4X20 0xc001 USB-LCD 4x20
+
+/* Jablotron products */
+product JABLOTRON PC60B 0x0001 PC-60B
+
+/* Jaton products */
+product JATON EDA 0x5704 Ethernet
+
+/* JVC products */
+product JVC GR_DX95 0x000a GR-DX95
+product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet
+
+/* JRC products */
+product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V
+
+/* Kawatsu products */
+product KAWATSU MH4000P 0x0003 MiniHub 4000P
+
+/* Keisokugiken Corp. products */
+product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ
+
+/* Kensington products */
+product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball
+product KENSINGTON TURBOBALL 0x1005 TurboBall
+
+/* Keyspan products */
+product KEYSPAN USA28_NF 0x0101 USA-28 serial Adapter (no firmware)
+product KEYSPAN USA28X_NF 0x0102 USA-28X serial Adapter (no firmware)
+product KEYSPAN USA19_NF 0x0103 USA-19 serial Adapter (no firmware)
+product KEYSPAN USA18_NF 0x0104 USA-18 serial Adapter (no firmware)
+product KEYSPAN USA18X_NF 0x0105 USA-18X serial Adapter (no firmware)
+product KEYSPAN USA19W_NF 0x0106 USA-19W serial Adapter (no firmware)
+product KEYSPAN USA19 0x0107 USA-19 serial Adapter
+product KEYSPAN USA19W 0x0108 USA-19W serial Adapter
+product KEYSPAN USA49W_NF 0x0109 USA-49W serial Adapter (no firmware)
+product KEYSPAN USA49W 0x010a USA-49W serial Adapter
+product KEYSPAN USA19QI_NF 0x010b USA-19QI serial Adapter (no firmware)
+product KEYSPAN USA19QI 0x010c USA-19QI serial Adapter
+product KEYSPAN USA19Q_NF 0x010d USA-19Q serial Adapter (no firmware)
+product KEYSPAN USA19Q 0x010e USA-19Q serial Adapter
+product KEYSPAN USA28 0x010f USA-28 serial Adapter
+product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial Adapter
+product KEYSPAN USA18 0x0111 USA-18 serial Adapter
+product KEYSPAN USA18X 0x0112 USA-18X serial Adapter
+product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial Adapter (no firmware)
+product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial Adapter (no firmware)
+product KEYSPAN USA28XA 0x0115 USA-28XA serial Adapter
+product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial Adapter (no firmware)
+product KEYSPAN USA18XA 0x0117 USA-18XA serial Adapter
+product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial Adapter (no firmware)
+product KEYSPAN USA19QW 0x0119 USA-19WQ serial Adapter
+product KEYSPAN USA19HA 0x0121 USA-19HS serial Adapter
+product KEYSPAN UIA10 0x0201 UIA-10 remote control
+product KEYSPAN UIA11 0x0202 UIA-11 remote control
+
+/* Kingston products */
+product KINGSTON XX1 0x0008 Ethernet
+product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet
+
+/* Kawasaki products */
+product KLSI DUH3E10BT 0x0008 USB Ethernet
+product KLSI DUH3E10BTN 0x0009 USB Ethernet
+
+/* Kodak products */
+product KODAK DC220 0x0100 Digital Science DC220
+product KODAK DC260 0x0110 Digital Science DC260
+product KODAK DC265 0x0111 Digital Science DC265
+product KODAK DC290 0x0112 Digital Science DC290
+product KODAK DC240 0x0120 Digital Science DC240
+product KODAK DC280 0x0130 Digital Science DC280
+
+/* Konica Corp. Products */
+product KONICA CAMERA 0x0720 Digital Color Camera
+
+/* KYE products */
+product KYE NICHE 0x0001 Niche mouse
+product KYE NETSCROLL 0x0003 Genius NetScroll mouse
+product KYE FLIGHT2000 0x1004 Flight 2000 joystick
+product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner
+
+/* Kyocera products */
+product KYOCERA FINECAM_S3X 0x0100 Finecam S3x
+product KYOCERA FINECAM_S4 0x0101 Finecam S4
+product KYOCERA FINECAM_S5 0x0103 Finecam S5
+product KYOCERA FINECAM_L3 0x0105 Finecam L3
+product KYOCERA AHK3001V 0x0203 AH-K3001V
+product KYOCERA2 CDMA_MSM_K 0x17da Qualcomm Kyocera CDMA Technologies MSM
+
+/* LaCie products */
+product LACIE HD 0xa601 Hard Disk
+product LACIE CDRW 0xa602 CD R/W
+
+/* Lexar products */
+product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader
+product LEXAR CF_READER 0xb002 USB CF Reader
+
+/* Lexmark products */
+product LEXMARK S2450 0x0009 Optra S 2450
+
+/* Linksys products */
+product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2
+product LINKSYS USB10TX1 0x200c USB10TX
+product LINKSYS USB10T 0x2202 USB10T Ethernet
+product LINKSYS USB100TX 0x2203 USB100TX Ethernet
+product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA
+product LINKSYS USB10TA 0x2206 USB10TA Ethernet
+product LINKSYS USB10TX2 0x400b USB10TX
+product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless Adapter
+product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 Ethernet
+product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 Wireless Adapter
+product LINKSYS4 USB1000 0x0039 USB1000
+
+/* Logitech products */
+product LOGITECH M2452 0x0203 M2452 keyboard
+product LOGITECH M4848 0x0301 M4848 mouse
+product LOGITECH PAGESCAN 0x040f PageScan
+product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web
+product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro
+product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express
+product LOGITECH QUICKCAM 0x0850 QuickCam
+product LOGITECH N43 0xc000 N43
+product LOGITECH N48 0xc001 N48 mouse
+product LOGITECH MBA47 0xc002 M-BA47 mouse
+product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse
+product LOGITECH BD58 0xc00c BD58 mouse
+product LOGITECH UN58A 0xc030 iFeel Mouse
+product LOGITECH UN53B 0xc032 iFeel MouseMan
+product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme
+product LOGITECH WMRPAD 0xc20a WingMan RumblePad
+product LOGITECH WMJOY 0xc281 WingMan Force joystick
+product LOGITECH BB13 0xc401 USB-PS/2 Trackball
+product LOGITECH RK53 0xc501 Cordless mouse
+product LOGITECH RB6 0xc503 Cordless keyboard
+product LOGITECH MX700 0xc506 Cordless optical mouse
+product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro
+
+/* Logitec Corp. products */
+product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2
+product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2
+
+/* Lucent products */
+product LUCENT EVALKIT 0x1001 USS-720 evaluation kit
+
+/* Luwen products */
+product LUWEN EASYDISK 0x0005 EasyDisc
+
+/* Macally products */
+product MACALLY MOUSE1 0x0101 mouse
+
+/* MCT Corp. */
+product MCT HUB0100 0x0100 Hub
+product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub
+product MCT USB232 0x0210 USB-232 Interface
+product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products
+
+/* Meizu Electronics */
+product MEIZU M6_SL 0x0140 MiniPlayer M6 (SL)
+
+/* Melco, Inc products */
+product MELCO LUATX1 0x0001 LUA-TX Ethernet
+product MELCO LUATX5 0x0005 LUA-TX Ethernet
+product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet
+product MELCO LUAKTX 0x0012 LUA-KTX Ethernet
+product MELCO DUBPXXG 0x001c USB-IDE Bridge: DUB-PxxG
+product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet
+product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN
+product MELCO KG54 0x0066 WLI-U2-KG54 WLAN
+product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN
+product MELCO NINWIFI 0x008b Nintendo Wi-Fi
+product MELCO PCOPRS1 0x00b3 PC-OP-RS1 RemoteStation
+product MELCO SG54HP 0x00d8 WLI-U2-SG54HP
+product MELCO G54HP 0x00d9 WLI-U2-G54HP
+product MELCO KG54L 0x00da WLI-U2-KG54L
+product MELCO SG54HG 0x00f4 WLI-U2-SG54HG
+
+/* Merlin products */
+product MERLIN V620 0x1110 Merlin V620
+
+/* MetaGeek products */
+product METAGEEK WISPY1B 0x083e MetaGeek Wi-Spy
+product METAGEEK WISPY24X 0x083f MetaGeek Wi-Spy 2.4x
+
+/* Metricom products */
+product METRICOM RICOCHET_GS 0x0001 Ricochet GS
+
+/* MGE UPS Systems */
+product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1
+product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2
+
+/* Micro Star International products */
+product MSI BT_DONGLE 0x1967 Bluetooth USB dongle
+product MSI UB11B 0x6823 UB11B
+product MSI RT2570 0x6861 RT2570
+product MSI RT2570_2 0x6865 RT2570
+product MSI RT2570_3 0x6869 RT2570
+product MSI RT2573_1 0x6874 RT2573
+product MSI RT2573_2 0x6877 RT2573
+product MSI RT2573_3 0xa861 RT2573
+product MSI RT2573_4 0xa874 RT2573
+
+/* Microsoft products */
+product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro
+product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse
+product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite
+product MICROSOFT DDS80 0x0014 Digital Sound System 80
+product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel
+product MICROSOFT INETPRO 0x001c Internet Keyboard Pro
+product MICROSOFT TBEXPLORER 0x0024 Trackball Explorer
+product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse
+product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro
+product MICROSOFT MN510 0x006e MN510 Wireless
+product MICROSOFT MN110 0x007a 10/100 USB NIC
+product MICROSOFT WLINTELLIMOUSE 0x008c Wireless Optical IntelliMouse
+product MICROSOFT WLNOTEBOOK 0x00b9 Wireless Optical Mouse (Model 1023)
+product MICROSOFT COMFORT3000 0x00d1 Comfort Optical Mouse 3000 (Model 1043)
+product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056)
+product MICROSOFT WLNOTEBOOK3 0x00d2 Wireless Optical Mouse 3000 (Model 1049)
+product MICROSOFT WLUSBMOUSE 0x00b9 Wireless USB Mouse
+product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN
+
+/* Microtech products */
+product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25
+product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50
+product MICROTECH DPCM 0x0006 USB CameraMate
+product MICROTECH FREECOM 0xfc01 Freecom USB-IDE
+
+/* Microtek products */
+product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner
+product MICROTEK X6U 0x0099 ScanMaker X6 - X6U
+product MICROTEK C6 0x009a Phantom C6 scanner
+product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner
+product MICROTEK V6USL 0x00a3 ScanMaker V6USL
+product MICROTEK V6USL2 0x80a3 ScanMaker V6USL
+product MICROTEK V6UL 0x80ac ScanMaker V6UL
+
+/* Microtune, Inc. products */
+product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle
+
+/* Midiman products */
+product MIDIMAN MIDISPORT2X2 0x1001 Midisport 2x2
+
+/* MindsAtWork products */
+product MINDSATWORK WALLET 0x0001 Digital Wallet
+
+/* Minolta Co., Ltd. */
+product MINOLTA 2300 0x4001 Dimage 2300
+product MINOLTA S304 0x4007 Dimage S304
+product MINOLTA X 0x4009 Dimage X
+product MINOLTA 5400 0x400e Dimage 5400
+product MINOLTA F300 0x4011 Dimage F300
+product MINOLTA E223 0x4017 Dimage E223
+
+/* Mitsumi products */
+product MITSUMI CDRRW 0x0000 CD-R/RW Drive
+product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle
+product MITSUMI FDD 0x6901 USB FDD
+
+/* Mobility products */
+product MOBILITY EA 0x0204 Ethernet
+product MOBILITY EASIDOCK 0x0304 EasiDock Ethernet
+
+/* MosChip products */
+product MOSCHIP MCS7703 0x7703 MCS7703 Serial Port Adapter
+product MOSCHIP MCS7830 0x7830 MCS7830 Ethernet
+
+/* Motorola products */
+product MOTOROLA MC141555 0x1555 MC141555 hub controller
+product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem
+product MOTOROLA2 A41XV32X 0x2a22 A41x/V32x Mobile Phones
+product MOTOROLA2 E398 0x4810 E398 Mobile Phone
+product MOTOROLA2 USBLAN 0x600c USBLAN
+product MOTOROLA2 USBLAN2 0x6027 USBLAN
+
+/* MultiTech products */
+product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem
+
+/* Mustek products */
+product MUSTEK 1200CU 0x0001 1200 CU scanner
+product MUSTEK 600CU 0x0002 600 CU scanner
+product MUSTEK 1200USB 0x0003 1200 USB scanner
+product MUSTEK 1200UB 0x0006 1200 UB scanner
+product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner
+product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner
+product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner
+product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner
+product MUSTEK 600USB 0x0873 600 USB scanner
+product MUSTEK MDC800 0xa800 MDC-800 digital camera
+
+/* M-Systems products */
+product MSYSTEMS DISKONKEY 0x0010 DiskOnKey
+product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey
+
+/* Myson products */
+product MYSON HEDEN 0x8818 USB-IDE
+product MYSON STARREADER 0x9920 USB flash card adapter
+
+/* National Semiconductor */
+product NATIONAL BEARPAW1200 0x1000 BearPaw 1200
+product NATIONAL BEARPAW2400 0x1001 BearPaw 2400
+
+/* NEC products */
+product NEC HUB 0x55aa hub
+product NEC HUB_B 0x55ab hub
+
+/* NEODIO products */
+product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller
+product NEODIO ND5010 0x5010 Multi-format Flash Controller
+
+/* Netac products */
+product NETAC CF_CARD 0x1060 USB-CF-Card
+product NETAC ONLYDISK 0x0003 OnlyDisk
+
+/* NetChip Technology Products */
+product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect
+product NETCHIP CLIK_40 0xa140 USB Clik! 40
+product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x
+
+/* Netgear products */
+product NETGEAR EA101 0x1001 Ethernet
+product NETGEAR EA101X 0x1002 Ethernet
+product NETGEAR FA101 0x1020 Ethernet 10/100, USB1.1
+product NETGEAR FA120 0x1040 USB 2.0 Ethernet
+product NETGEAR WG111V2_2 0x4240 PrismGT USB 2.0 WLAN
+product NETGEAR WG111U 0x4300 WG111U
+product NETGEAR WG111U_NF 0x4301 WG111U (no firmware)
+product NETGEAR WG111V2 0x6a00 WG111V2
+product NETGEAR2 MA101 0x4100 MA101
+product NETGEAR2 MA101B 0x4102 MA101 Rev B
+product NETGEAR3 WG111T 0x4250 WG111T
+product NETGEAR3 WG111T_NF 0x4251 WG111T (no firmware)
+product NETGEAR3 WPN111 0x5f00 WPN111
+product NETGEAR3 WPN111_NF 0x5f01 WPN111 (no firmware)
+
+/* Nikon products */
+product NIKON E990 0x0102 Digital Camera E990
+product NIKON LS40 0x4000 CoolScan LS40 ED
+product NIKON D300 0x041a Digital Camera D300
+
+/* NovaTech Products */
+product NOVATECH NV902 0x9020 NovaTech NV-902W
+product NOVATECH RT2573 0x9021 RT2573
+
+/* Novatel Wireless products */
+product NOVATEL V640 0x1100 Merlin V620
+product NOVATEL CDMA_MODEM 0x1110 Novatel Wireless Merlin CDMA
+product NOVATEL V620 0x1110 Merlin V620
+product NOVATEL V740 0x1120 Merlin V740
+product NOVATEL V720 0x1130 Merlin V720
+product NOVATEL U740 0x1400 Merlin U740
+product NOVATEL U740_2 0x1410 Merlin U740
+product NOVATEL U870 0x1420 Merlin U870
+product NOVATEL XU870 0x1430 Merlin XU870
+product NOVATEL X950D 0x1450 Merlin X950D
+product NOVATEL ES620 0x2100 ES620 CDMA
+product NOVATEL U720 0x2110 Merlin U720
+product NOVATEL U727 0x4100 Merlin U727 CDMA
+product NOVATEL MC950D 0x4400 Novatel MC950D HSUPA
+product NOVATEL ZEROCD 0x5010 Novatel ZeroCD
+product NOVATEL ZEROCD2 0x5030 Novatel ZeroCD
+product NOVATEL U760 0x6000 Novatel U760
+product NOVATEL2 FLEXPACKGPS 0x0100 NovAtel FlexPack GPS receiver
+
+/* Merlin products */
+product MERLIN V620 0x1110 Merlin V620
+
+/* Olympus products */
+product OLYMPUS C1 0x0102 C-1 Digital Camera
+product OLYMPUS C700 0x0105 C-700 Ultra Zoom
+
+/* OmniVision Technologies, Inc. products */
+product OMNIVISION OV511 0x0511 OV511 Camera
+product OMNIVISION OV511PLUS 0xa511 OV511+ Camera
+
+/* OnSpec Electronic, Inc. */
+product ONSPEC SDS_HOTFIND_D 0x0400 SDS-infrared.com Hotfind-D Infrared Camera
+product ONSPEC MDCFE_B_CF_READER 0xa000 MDCFE-B USB CF Reader
+product ONSPEC CFMS_RW 0xa001 SIIG/Datafab Memory Stick+CF Reader/Writer
+product ONSPEC READER 0xa003 Datafab-based Reader
+product ONSPEC CFSM_READER 0xa005 PNY/Datafab CF+SM Reader
+product ONSPEC CFSM_READER2 0xa006 Simple Tech/Datafab CF+SM Reader
+product ONSPEC MDSM_B_READER 0xa103 MDSM-B reader
+product ONSPEC CFSM_COMBO 0xa109 USB to CF + SM Combo (LC1)
+product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader
+product ONSPEC2 IMAGEMATE_SDDR55 0xa103 ImageMate SDDR55
+
+/* Option products */
+product OPTION VODAFONEMC3G 0x5000 Vodafone Mobile Connect 3G datacard
+product OPTION GT3G 0x6000 GlobeTrotter 3G datacard
+product OPTION GT3GQUAD 0x6300 GlobeTrotter 3G QUAD datacard
+product OPTION GT3GPLUS 0x6600 GlobeTrotter 3G+ datacard
+product OPTION GTICON322 0xd033 GlobeTrotter Icon322 storage
+product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem
+product OPTION GTMAXHSUPA 0x7001 GlobeTrotter HSUPA
+
+/* OQO */
+product OQO WIFI01 0x0002 model 01 WiFi interface
+product OQO BT01 0x0003 model 01 Bluetooth interface
+product OQO ETHER01PLUS 0x7720 model 01+ Ethernet
+product OQO ETHER01 0x8150 model 01 Ethernet interface
+
+/* Palm Computing, Inc. product */
+product PALM SERIAL 0x0080 USB Serial
+product PALM M500 0x0001 Palm m500
+product PALM M505 0x0002 Palm m505
+product PALM M515 0x0003 Palm m515
+product PALM I705 0x0020 Palm i705
+product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z
+product PALM M125 0x0040 Palm m125
+product PALM M130 0x0050 Palm m130
+product PALM TUNGSTEN_T 0x0060 Palm Tungsten T
+product PALM ZIRE31 0x0061 Palm Zire 31
+product PALM ZIRE 0x0070 Palm Zire
+
+/* Panasonic products */
+product PANASONIC LS120CAM 0x0901 LS-120 Camera
+product PANASONIC KXL840AN 0x0d01 CD-R Drive KXL-840AN
+product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN
+product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN
+product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW
+product PANASONIC SDCAAE 0x1b00 MultiMediaCard
+
+/* Peracom products */
+product PERACOM SERIAL1 0x0001 Serial
+product PERACOM ENET 0x0002 Ethernet
+product PERACOM ENET3 0x0003 At Home Ethernet
+product PERACOM ENET2 0x0005 Ethernet
+
+/* Philips products */
+product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System
+product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System
+product PHILIPS HUB 0x0201 hub
+product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera
+product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera
+product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System
+product PHILIPS SNU5600 0x1236 SNU5600
+product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit
+product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player
+
+/* Philips Semiconductor products */
+product PHILIPSSEMI HUB1122 0x1122 hub
+
+/* P.I. Engineering products */
+product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter
+
+/* Planex Communications products */
+product PLANEX GW_US11H 0x14ea GW-US11H WLAN
+product PLANEX2 GW_US11S 0x3220 GW-US11S WLAN
+product PLANEX2 GW_US54GXS 0x5303 GW-US54GXS WLAN
+product PLANEX2 GWUS54HP 0xab01 GW-US54HP
+product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2
+product PLANEX2 GWUS54SG 0xc002 GW-US54SG
+product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL
+product PLANEX2 GWUS54GD 0xed01 GW-US54GD
+product PLANEX2 GWUSMM 0xed02 GW-USMM
+product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ
+product PLANEX3 GU1000T 0xab11 GU-1000T
+product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini
+
+/* Plextor Corp. */
+product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U
+
+/* PLX products */
+product PLX TESTBOARD 0x9060 test board
+
+/* PNY products */
+product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive
+
+/* PortGear products */
+product PORTGEAR EA8 0x0008 Ethernet
+product PORTGEAR EA9 0x0009 Ethernet
+
+/* Portsmith products */
+product PORTSMITH EEA 0x3003 Express Ethernet
+
+/* Primax products */
+product PRIMAX G2X300 0x0300 G2-200 scanner
+product PRIMAX G2E300 0x0301 G2E-300 scanner
+product PRIMAX G2300 0x0302 G2-300 scanner
+product PRIMAX G2E3002 0x0303 G2E-300 scanner
+product PRIMAX 9600 0x0340 Colorado USB 9600 scanner
+product PRIMAX 600U 0x0341 Colorado 600u scanner
+product PRIMAX 6200 0x0345 Visioneer 6200 scanner
+product PRIMAX 19200 0x0360 Colorado USB 19200 scanner
+product PRIMAX 1200U 0x0361 Colorado 1200u scanner
+product PRIMAX G600 0x0380 G2-600 scanner
+product PRIMAX 636I 0x0381 ReadyScan 636i
+product PRIMAX G2600 0x0382 G2-600 scanner
+product PRIMAX G2E600 0x0383 G2E-600 scanner
+product PRIMAX COMFORT 0x4d01 Comfort
+product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box
+product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1
+
+/* Prolific products */
+product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface
+product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface
+product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2)
+product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A)
+product PROLIFIC PL2305 0x2305 Parallel printer
+product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller
+product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface
+product PROLIFIC PHAROS 0xaaa0 Prolific Pharos
+product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial Adapter (IODATA USB-RSAQ3)
+product PROLIFIC2 WSIM 0x2001 Willcom WSIM
+
+/* Putercom products */
+product PUTERCOM UPA100 0x047e USB-1284 BRIDGE
+
+/* Qcom products */
+product QCOM RT2573 0x6196 RT2573
+product QCOM RT2573_2 0x6229 RT2573
+
+/* Qualcomm products */
+product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone
+product QUALCOMM2 RWT_FCT 0x3100 RWT FCT-CDMA 2000 1xRTT modem
+product QUALCOMM2 CDMA_MSM 0x3196 CDMA Technologies MSM modem
+product QUALCOMMINC CDMA_MSM 0x0001 CDMA Technologies MSM modem
+product QUALCOMMINC ZTE_STOR 0x2000 USB ZTE Storage
+product QUALCOMMINC AC8700 0xfffe CDMA 1xEVDO USB modem
+
+/* Qtronix products */
+product QTRONIX 980N 0x2011 Scorpion-980N keyboard
+
+/* Quickshot products */
+product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad
+
+/* Radio Shack */
+product RADIOSHACK USBCABLE 0x4026 USB to Serial Cable
+
+/* Rainbow Technologies products */
+product RAINBOW IKEY2000 0x1200 i-Key 2000
+
+/* Ralink Technology products */
+product RALINK RT2570 0x1706 RT2500USB Wireless Adapter
+product RALINK RT2570_2 0x2570 RT2500USB Wireless Adapter
+product RALINK RT2573 0x2573 RT2501USB Wireless Adapter
+product RALINK RT2671 0x2671 RT2601USB Wireless Adapter
+product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter
+product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter
+
+/* ReakTek products */
+/* Green House and CompUSA OEM this part */
+product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet
+product REALTEK RTL8187 0x8187 RTL8187 Wireless Adapter
+
+/* Ricoh products */
+product RICOH VGPVCC2 0x1830 VGP-VCC2 Camera
+product RICOH VGPVCC3 0x1832 VGP-VCC3 Camera
+product RICOH VGPVCC2_2 0x1833 VGP-VCC2 Camera
+product RICOH VGPVCC2_3 0x1834 VGP-VCC2 Camera
+product RICOH VGPVCC7 0x183a VGP-VCC7 Camera
+product RICOH VGPVCC8 0x183b VGP-VCC8 Camera
+
+/* Roland products */
+product ROLAND UM1 0x0009 UM-1 MIDI I/F
+product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native)
+product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic)
+
+/* Rockfire products */
+product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB
+
+/* RATOC Systems products */
+product RATOC REXUSB60 0xb000 REX-USB60
+
+/* Sagem products */
+product SAGEM USBSERIAL 0x0027 USB-Serial Controller
+product SAGEM XG760A 0x004a XG-760A
+product SAGEM XG76NA 0x0062 XG-76NA
+
+/* Samsung products */
+product SAMSUNG ML6060 0x3008 ML-6060 laser printer
+product SAMSUNG YP_U2 0x5050 YP-U2 MP3 Player
+product SAMSUNG I500 0x6601 I500 Palm USB Phone
+
+/* Samsung Techwin products */
+product SAMSUNG_TECHWIN DIGIMAX_410 0x000a Digimax 410
+
+/* SanDisk products */
+product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a
+product SANDISK SDDR31 0x0002 ImageMate SDDR-31
+product SANDISK SDDR05 0x0005 ImageMate SDDR-05
+product SANDISK SDDR12 0x0100 ImageMate SDDR-12
+product SANDISK SDDR09 0x0200 ImageMate SDDR-09
+product SANDISK SDDR75 0x0810 ImageMate SDDR-75
+product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB
+product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB
+product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB
+
+/* Sanyo Electric products */
+product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone
+
+/* ScanLogic products */
+product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter
+product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner
+
+/* Senao products */
+product SENAO NUB8301 0x2000 NUB-8301
+
+/* ShanTou products */
+product SHANTOU ST268 0x0268 ST268
+product SHANTOU DM9601 0x9601 DM 9601
+
+/* Shark products */
+product SHARK PA 0x0400 Pocket Adapter
+
+/* Sharp products */
+product SHARP SL5500 0x8004 Zaurus SL-5500 PDA
+product SHARP SLA300 0x8005 Zaurus SL-A300 PDA
+product SHARP SL5600 0x8006 Zaurus SL-5600 PDA
+product SHARP SLC700 0x8007 Zaurus SL-C700 PDA
+product SHARP SLC750 0x9031 Zaurus SL-C750 PDA
+product SHARP WZERO3ES 0x9123 W-ZERO3 ES Smartphone
+
+/* Shuttle Technology products */
+product SHUTTLE EUSB 0x0001 E-USB Bridge
+product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge
+product SHUTTLE SDDR09 0x0003 ImageMate SDDR09
+product SHUTTLE EUSBCFSM 0x0005 eUSB SmartMedia / CompactFlash Adapter
+product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter
+product SHUTTLE HIFD 0x0007 Sony Hifd
+product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter
+product SHUTTLE CF 0x000a eUSB CompactFlash Adapter
+product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge
+product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge
+product SHUTTLE CDRW 0x0101 CD-RW Device
+product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader
+
+/* Siemens products */
+product SIEMENS SPEEDSTREAM 0x1001 SpeedStream
+product SIEMENS SPEEDSTREAM22 0x1022 SpeedStream 1022
+product SIEMENS2 WLL013 0x001b WLL013
+product SIEMENS2 ES75 0x0034 GSM module MC35
+product SIEMENS2 WL54G 0x3c06 54g USB Network Adapter
+product SIEMENS3 SX1 0x0001 SX1
+product SIEMENS3 X65 0x0003 X65
+product SIEMENS3 X75 0x0004 X75
+
+/* Sierra Wireless products */
+product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580
+product SIERRA AIRCARD595 0x0019 Sierra Wireless AirCard 595
+product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U
+product SIERRA AC597E 0x0021 Sierra Wireless AirCard 597E
+product SIERRA C597 0x0023 Sierra Wireless Compass 597
+product SIERRA AC875 0x6820 Sierra Wireless AirCard 875
+product SIERRA AC880 0x6850 Sierra Wireless AirCard 880
+product SIERRA AC881 0x6851 Sierra Wireless AirCard 881
+product SIERRA AC880E 0x6852 Sierra Wireless AirCard 880E
+product SIERRA AC881E 0x6853 Sierra Wireless AirCard 881E
+product SIERRA AC880U 0x6855 Sierra Wireless AirCard 880U
+product SIERRA AC881U 0x6856 Sierra Wireless AirCard 881U
+product SIERRA AC885U 0x6880 Sierra Wireless AirCard 885U
+product SIERRA EM5625 0x0017 EM5625
+product SIERRA MC5720 0x0218 MC5720 Wireless Modem
+product SIERRA MC5720_2 0x0018 MC5720
+product SIERRA MC5725 0x0020 MC5725
+product SIERRA MINI5725 0x0220 Sierra Wireless miniPCI 5275
+product SIERRA MC8755_2 0x6802 MC8755
+product SIERRA MC8765 0x6803 MC8765
+product SIERRA MC8755 0x6804 MC8755
+product SIERRA AC875U 0x6812 AC875U HSDPA USB Modem
+product SIERRA MC8755_3 0x6813 MC8755 HSDPA
+product SIERRA MC8775_2 0x6815 MC8775
+product SIERRA AIRCARD875 0x6820 Aircard 875 HSDPA
+product SIERRA MC8780 0x6832 MC8780
+product SIERRA MC8781 0x6833 MC8781
+product SIERRA TRUINSTALL 0x0fff Aircard Tru Installer
+
+/* Sigmatel products */
+product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player
+
+/* SIIG products */
+/* Also: Omnidirectional Control Technology products */
+product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader
+product SIIG WINTERREADER 0x0330 WINTERREADER Reader
+product SIIG2 USBTOETHER 0x0109 USB TO Ethernet
+product SIIG2 US2308 0x0421 Serial
+
+/* Silicom products */
+product SILICOM U2E 0x0001 U2E
+product SILICOM GPE 0x0002 Psion Gold Port Ethernet
+
+/* SI Labs */
+product SILABS POLOLU 0x803b Pololu Serial
+product SILABS ARGUSISP 0x8066 Argussoft ISP
+product SILABS CRUMB128 0x807a Crumb128 board
+product SILABS DEGREE 0x80ca Degree Controls Inc
+product SILABS TRAQMATE 0x80ed Track Systems Traqmate
+product SILABS SUUNTO 0x80f6 Suunto Sports Instrument
+product SILABS BURNSIDE 0x813d Burnside Telecon Deskmobile
+product SILABS HELICOM 0x815e Helicomm IP-Link 1220-DVM
+product SILABS CP2102 0xea60 SILABS USB UART
+product SILABS LIPOWSKY_JTAG 0x81c8 Lipowsky Baby-JTAG
+product SILABS LIPOWSKY_LIN 0x81e2 Lipowsky Baby-LIN
+product SILABS LIPOWSKY_HARP 0x8218 Lipowsky HARP-1
+product SILABS CP2102 0xea60 SILABS USB UARTa
+product SILABS CP210X_2 0xea61 CP210x Serial
+product SILABS2 DCU11CLONE 0xaa26 DCU-11 clone
+
+/* Silicon Portals Inc. */
+product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware)
+product SILICONPORTALS YAPPHONE 0x0201 YAP Phone
+
+/* Sirius Technologies products */
+product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB
+
+/* Sitecom products */
+product SITECOM LN029 0x182d USB 2.0 Ethernet
+product SITECOM SERIAL 0x2068 USB to serial cable (v2)
+product SITECOM2 WL022 0x182d WL-022
+
+/* Sitecom Europe products */
+product SITECOMEU LN028 0x061c LN-028
+product SITECOMEU WL113 0x9071 WL-113
+product SITECOMEU ZD1211B 0x9075 ZD1211B
+product SITECOMEU WL172 0x90ac WL-172
+product SITECOMEU WL113R2 0x9712 WL-113 rev 2
+
+/* Skanhex Technology products */
+product SKANHEX MD_7425 0x410a MD 7425 Camera
+product SKANHEX SX_520Z 0x5200 SX 520z Camera
+
+/* SmartBridges products */
+product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB Ethernet
+product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Ethernet
+
+/* SMC products */
+product SMC 2102USB 0x0100 10Mbps Ethernet
+product SMC 2202USB 0x0200 10/100 Ethernet
+product SMC 2206USB 0x0201 EZ Connect USB Ethernet
+product SMC 2862WG 0xee13 EZ Connect Wireless Adapter
+product SMC2 2020HUB 0x2020 USB Hub
+product SMC3 2662WUSB 0xa002 2662W-AR Wireless
+
+/* SOHOware products */
+product SOHOWARE NUB100 0x9100 10/100 USB Ethernet
+product SOHOWARE NUB110 0x9110 10/100 USB Ethernet
+
+/* SOLID YEAR products */
+product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard
+
+/* SONY products */
+product SONY DSC 0x0010 DSC cameras
+product SONY MS_NW_MS7 0x0025 Memorystick NW-MS7
+product SONY PORTABLE_HDD_V2 0x002b Portable USB Harddrive V2
+product SONY MSACUS1 0x002d Memorystick MSAC-US1
+product SONY HANDYCAM 0x002e Handycam
+product SONY MSC 0x0032 MSC memory stick slot
+product SONY CLIE_35 0x0038 Sony Clie v3.5
+product SONY MS_PEG_N760C 0x0058 PEG N760c Memorystick
+product SONY CLIE_40 0x0066 Sony Clie v4.0
+product SONY MS_MSC_U03 0x0069 Memorystick MSC-U03
+product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot
+product SONY CLIE_S360 0x0095 Sony Clie s360
+product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot
+product SONY CLIE_41 0x009a Sony Clie v4.1
+product SONY CLIE_NX60 0x00da Sony Clie nx60
+product SONY CLIE_TH55 0x0144 Sony Clie th55
+product SONY CLIE_TJ37 0x0169 Sony Clie tj37
+product SONY RF_RECEIVER 0x01db Sony RF mouse/kbd Receiver VGP-WRC1
+
+/* Sony Ericsson products */
+product SONYERICSSON DCU10 0x0528 USB Cable
+
+/* SOURCENEXT products */
+product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8
+product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger
+
+/* SparkLAN products */
+product SPARKLAN RT2573 0x0004 RT2573
+
+/* Sphairon Access Systems GmbH products */
+product SPHAIRON UB801R 0x0110 UB801R
+
+/* Stelera Wireless products */
+product STELERA ZEROCD 0x1000 Zerocd Installer
+product STELERA C105 0x1002 Stelera/Bandrish C105 USB
+
+/* STMicroelectronics products */
+product STMICRO BIOCPU 0x2016 Biometric Coprocessor
+product STMICRO COMMUNICATOR 0x7554 USB Communicator
+
+/* STSN products */
+product STSN STSN0001 0x0001 Internet Access Device
+
+/* SUN Corporation products */
+product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2
+product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1
+product SUNTAC VS10U 0x0009 SUNTAC Slipper U
+product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity
+product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3
+product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4
+
+/* Sun Microsystems products */
+product SUN KEYBOARD 0x0005 Type 6 USB keyboard
+/* XXX The above is a North American PC style keyboard possibly */
+product SUN MOUSE 0x0100 Type 6 USB mouse
+
+/* Supra products */
+product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem
+product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem
+product DIAMOND2 RIO600USB 0x5001 Rio 600 USB
+product DIAMOND2 RIO800USB 0x5002 Rio 800 USB
+
+/* Surecom Technology products */
+product SURECOM RT2570 0x11f3 RT2570
+product SURECOM RT2573 0x31f3 RT2573
+
+/* Sweex products */
+product SWEEX ZD1211 0x1809 ZD1211
+
+/* System TALKS, Inc. */
+product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL
+
+/* Tapwave products */
+product TAPWAVE ZODIAC 0x0100 Zodiac
+
+/* Taugagreining products */
+product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB)
+
+/* TDK products */
+product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664
+product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464
+product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400
+product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400
+product TDK BT_DONGLE 0x0309 Bluetooth USB dongle
+
+/* TEAC products */
+product TEAC FD05PUB 0x0000 FD-05PUB floppy
+
+/* Tekram Technology products */
+product TEKRAM QUICKWLAN 0x1630 QuickWLAN
+product TEKRAM ZD1211_1 0x5630 ZD1211
+product TEKRAM ZD1211_2 0x6630 ZD1211
+
+/* Telex Communications products */
+product TELEX MIC1 0x0001 Enhanced USB Microphone
+
+/* Ten X Technology, Inc. */
+product TENX UAUDIO0 0xf211 USB audio headset
+
+/* Texas Intel products */
+product TI UTUSB41 0x1446 UT-USB41 hub
+product TI TUSB2046 0x2046 TUSB2046 hub
+
+/* Thrustmaster products */
+product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad
+
+/* Topre Corporation products */
+product TOPRE HHKB 0x0100 HHKB Professional
+
+/* Toshiba Corporation products */
+product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740
+
+/* Trek Technology products */
+product TREK THUMBDRIVE 0x1111 ThumbDrive
+product TREK MEMKEY 0x8888 IBM USB Memory Key
+product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB
+
+/* Tripp-Lite products */
+product TRIPPLITE U209 0x2008 Serial
+
+/* Trumpion products */
+product TRUMPION T33520 0x1001 T33520 USB Flash Card Controller
+product TRUMPION C3310 0x1100 Comotron C3310 MP3 player
+product TRUMPION MP3 0x1200 MP3 player
+
+/* TwinMOS */
+product TWINMOS G240 0xa006 G240
+product TWINMOS MDIV 0x1325 Memory Disk IV
+
+/* Ubiquam products */
+product UBIQUAM UALL 0x3100 CDMA 1xRTT USB Modem (U-100/105/200/300/520)
+
+/* Ultima products */
+product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner
+
+/* UMAX products */
+product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner
+product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner
+product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner
+product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner
+product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner
+product UMAX ASTRA3400 0x0060 Astra 3400 Scanner
+
+/* U-MEDIA Communications products */
+product UMEDIA TEW444UBEU 0x3006 TEW-444UB EU
+product UMEDIA TEW444UBEU_NF 0x3007 TEW-444UB EU (no firmware)
+product UMEDIA TEW429UB_A 0x300a TEW-429UB_A
+product UMEDIA TEW429UB 0x300b TEW-429UB
+product UMEDIA TEW429UBC1 0x300d TEW-429UB C1
+product UMEDIA ALL0298V2 0x3204 ALL0298 v2
+product UMEDIA AR5523_2 0x3205 AR5523
+product UMEDIA AR5523_2_NF 0x3206 AR5523 (no firmware)
+
+/* Universal Access products */
+product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter
+
+/* U.S. Robotics products */
+product USR USR5423 0x0121 USR5423 WLAN
+
+/* VIA Technologies products */
+product VIA USB2IDEBRIDGE 0x6204 USB 2.0 IDE Bridge
+
+/* USI products */
+product USI MC60 0x10c5 MC60 Serial
+
+/* VidzMedia products */
+product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H
+
+/* Vision products */
+product VISION VC6452V002 0x0002 CPiA Camera
+
+/* Visioneer products */
+product VISIONEER 7600 0x0211 OneTouch 7600
+product VISIONEER 5300 0x0221 OneTouch 5300
+product VISIONEER 3000 0x0224 Scanport 3000
+product VISIONEER 6100 0x0231 OneTouch 6100
+product VISIONEER 6200 0x0311 OneTouch 6200
+product VISIONEER 8100 0x0321 OneTouch 8100
+product VISIONEER 8600 0x0331 OneTouch 8600
+
+/* Vivitar products */
+product VIVITAR 35XX 0x0003 Vivicam 35Xx
+
+/* VTech products */
+product VTECH RT2570 0x3012 RT2570
+product VTECH ZD1211B 0x3014 ZD1211B
+
+/* Wacom products */
+product WACOM CT0405U 0x0000 CT-0405-U Tablet
+product WACOM GRAPHIRE 0x0010 Graphire
+product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5
+product WACOM INTUOSA5 0x0021 Intuos A5
+product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet
+/* WCH products*/
+product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge
+/* Western Digital products */
+product WESTERN COMBO 0x0200 Firewire USB Combo
+product WESTERN EXTHDD 0x0400 External HDD
+product WESTERN HUB 0x0500 USB HUB
+product WESTERN MYBOOK 0x0901 MyBook External HDD
+
+/* Windbond Electronics */
+product WINBOND UH104 0x5518 4-port USB Hub
+
+/* WinMaxGroup products */
+product WINMAXGROUP FLASH64MC 0x6660 USB Flash Disk 64M-C
+
+/* Wistron NeWeb products */
+product WISTRONNEWEB UR045G 0x0427 PrismGT USB 2.0 WLAN
+product WISTRONNEWEB UR055G 0x0711 UR055G
+product WISTRONNEWEB AR5523_1 0x0826 AR5523
+product WISTRONNEWEB AR5523_1_NF 0x0827 AR5523 (no firmware)
+product WISTRONNEWEB AR5523_2 0x082a AR5523
+product WISTRONNEWEB AR5523_2_NF 0x0829 AR5523 (no firmware)
+
+/* Xerox products */
+product XEROX WCM15 0xffef WorkCenter M15
+
+/* Xirlink products */
+product XIRLINK PCCAM 0x8080 IBM PC Camera
+
+/* Xyratex products */
+product XYRATEX PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN
+product XYRATEX PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN
+
+/* Y-E Data products */
+product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U
+
+/* Yamaha products */
+product YAMAHA UX256 0x1000 UX256 MIDI I/F
+product YAMAHA UX96 0x1008 UX96 MIDI I/F
+product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router
+product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router
+product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router
+product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router
+
+/* Yano products */
+product YANO U640MO 0x0101 U640MO-03
+product YANO FW800HD 0x05fc METALWEAR-HDD
+
+/* Yiso Wireless Co. products */
+product YISO C893 0xc893 CDMA 2000 1xEVDO PC Card
+
+/* Z-Com products */
+product ZCOM M4Y750 0x0001 M4Y-750
+product ZCOM XI725 0x0002 XI-725/726
+product ZCOM XI735 0x0005 XI-735
+product ZCOM XG703A 0x0008 PrismGT USB 2.0 WLAN
+product ZCOM ZD1211 0x0011 ZD1211
+product ZCOM AR5523 0x0012 AR5523
+product ZCOM AR5523_NF 0x0013 AR5523 driver (no firmware)
+product ZCOM ZD1211B 0x001a ZD1211B
+
+/* Zinwell products */
+product ZINWELL RT2570 0x0260 RT2570
+
+/* Zoom Telephonics, Inc. products */
+product ZOOM 2986L 0x9700 2986L Fax modem
+
+/* Zoran Microelectronics products */
+product ZORAN EX20DSC 0x4343 Digital Camera EX-20 DSC
+
+/* Zydas Technology Corporation products */
+product ZYDAS ZD1211 0x1211 ZD1211 WLAN abg
+product ZYDAS ZD1211B 0x1215 ZD1211B
+
+/* ZyXEL Communication Co. products */
+product ZYXEL OMNI56K 0x1500 Omni 56K Plus
+product ZYXEL 980N 0x2011 Scorpion-980N keyboard
+product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220
+product ZYXEL G200V2 0x3407 G-200 v2
+product ZYXEL AG225H 0x3409 AG-225H
+product ZYXEL M202 0x340a M-202
+product ZYXEL G220V2 0x340f G-220 v2
+product ZYXEL G202 0x3410 G-202
diff --git a/sys/legacy/dev/usb/usbdi.c b/sys/legacy/dev/usb/usbdi.c
new file mode 100644
index 0000000..a733bbf
--- /dev/null
+++ b/sys/legacy/dev/usb/usbdi.c
@@ -0,0 +1,1383 @@
+/* $NetBSD: usbdi.c,v 1.106 2004/10/24 12:52:40 augustss Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include "usb_if.h"
+#if defined(DIAGNOSTIC) && defined(__i386__)
+#include <machine/cpu.h>
+#endif
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usb_quirks.h>
+
+#include "usb_if.h"
+#define delay(d) DELAY(d)
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+static usbd_status usbd_ar_pipe(usbd_pipe_handle pipe);
+static void usbd_do_request_async_cb
+ (usbd_xfer_handle, usbd_private_handle, usbd_status);
+static void usbd_start_next(usbd_pipe_handle pipe);
+static usbd_status usbd_open_pipe_ival
+ (usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *, int);
+static int usbd_xfer_isread(usbd_xfer_handle xfer);
+static void usbd_start_transfer(void *arg, bus_dma_segment_t *segs, int nseg,
+ int error);
+static void usbd_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg,
+ int error);
+
+static int usbd_nbuses = 0;
+
+void
+usbd_init(void)
+{
+ usbd_nbuses++;
+}
+
+void
+usbd_finish(void)
+{
+ --usbd_nbuses;
+}
+
+static __inline int
+usbd_xfer_isread(usbd_xfer_handle xfer)
+{
+ if (xfer->rqflags & URQ_REQUEST)
+ return (xfer->request.bmRequestType & UT_READ);
+ else
+ return (xfer->pipe->endpoint->edesc->bEndpointAddress &
+ UE_DIR_IN);
+}
+
+#ifdef USB_DEBUG
+void
+usbd_dump_iface(struct usbd_interface *iface)
+{
+ printf("usbd_dump_iface: iface=%p\n", iface);
+ if (iface == NULL)
+ return;
+ printf(" device=%p idesc=%p index=%d altindex=%d priv=%p\n",
+ iface->device, iface->idesc, iface->index, iface->altindex,
+ iface->priv);
+}
+
+void
+usbd_dump_device(struct usbd_device *dev)
+{
+ printf("usbd_dump_device: dev=%p\n", dev);
+ if (dev == NULL)
+ return;
+ printf(" bus=%p default_pipe=%p\n", dev->bus, dev->default_pipe);
+ printf(" address=%d config=%d depth=%d speed=%d self_powered=%d "
+ "power=%d langid=%d\n",
+ dev->address, dev->config, dev->depth, dev->speed,
+ dev->self_powered, dev->power, dev->langid);
+}
+
+void
+usbd_dump_endpoint(struct usbd_endpoint *endp)
+{
+ printf("usbd_dump_endpoint: endp=%p\n", endp);
+ if (endp == NULL)
+ return;
+ printf(" edesc=%p refcnt=%d\n", endp->edesc, endp->refcnt);
+ if (endp->edesc)
+ printf(" bEndpointAddress=0x%02x\n",
+ endp->edesc->bEndpointAddress);
+}
+
+void
+usbd_dump_queue(usbd_pipe_handle pipe)
+{
+ usbd_xfer_handle xfer;
+
+ printf("usbd_dump_queue: pipe=%p\n", pipe);
+ STAILQ_FOREACH(xfer, &pipe->queue, next) {
+ printf(" xfer=%p\n", xfer);
+ }
+}
+
+void
+usbd_dump_pipe(usbd_pipe_handle pipe)
+{
+ printf("usbd_dump_pipe: pipe=%p\n", pipe);
+ if (pipe == NULL)
+ return;
+ usbd_dump_iface(pipe->iface);
+ usbd_dump_device(pipe->device);
+ usbd_dump_endpoint(pipe->endpoint);
+ printf(" (usbd_dump_pipe:)\n refcnt=%d running=%d aborting=%d\n",
+ pipe->refcnt, pipe->running, pipe->aborting);
+ printf(" intrxfer=%p, repeat=%d, interval=%d\n",
+ pipe->intrxfer, pipe->repeat, pipe->interval);
+}
+#endif
+
+usbd_status
+usbd_open_pipe(usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe)
+{
+ return (usbd_open_pipe_ival(iface, address, flags, pipe,
+ USBD_DEFAULT_INTERVAL));
+}
+
+usbd_status
+usbd_open_pipe_ival(usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe, int ival)
+{
+ usbd_pipe_handle p;
+ struct usbd_endpoint *ep;
+ usbd_status err;
+ int i;
+
+ DPRINTFN(3,("usbd_open_pipe: iface=%p address=0x%x flags=0x%x\n",
+ iface, address, flags));
+
+ for (i = 0; i < iface->idesc->bNumEndpoints; i++) {
+ ep = &iface->endpoints[i];
+ if (ep->edesc == NULL)
+ return (USBD_IOERROR);
+ if (ep->edesc->bEndpointAddress == address)
+ goto found;
+ }
+ return (USBD_BAD_ADDRESS);
+ found:
+ if ((flags & USBD_EXCLUSIVE_USE) && ep->refcnt != 0)
+ return (USBD_IN_USE);
+ err = usbd_setup_pipe(iface->device, iface, ep, ival, &p);
+ if (err)
+ return (err);
+ LIST_INSERT_HEAD(&iface->pipes, p, next);
+ *pipe = p;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_open_pipe_intr(usbd_interface_handle iface, u_int8_t address,
+ u_int8_t flags, usbd_pipe_handle *pipe,
+ usbd_private_handle priv, void *buffer, u_int32_t len,
+ usbd_callback cb, int ival)
+{
+ usbd_status err;
+ usbd_xfer_handle xfer;
+ usbd_pipe_handle ipipe;
+
+ DPRINTFN(3,("usbd_open_pipe_intr: address=0x%x flags=0x%x len=%d\n",
+ address, flags, len));
+
+ err = usbd_open_pipe_ival(iface, address, USBD_EXCLUSIVE_USE,
+ &ipipe, ival);
+ if (err)
+ return (err);
+ xfer = usbd_alloc_xfer(iface->device);
+ if (xfer == NULL) {
+ err = USBD_NOMEM;
+ goto bad1;
+ }
+ usbd_setup_xfer(xfer, ipipe, priv, buffer, len, flags,
+ USBD_NO_TIMEOUT, cb);
+ ipipe->intrxfer = xfer;
+ ipipe->repeat = 1;
+ *pipe = ipipe;
+ err = usbd_transfer(xfer);
+ if (err != USBD_IN_PROGRESS && err)
+ goto bad2;
+ return (USBD_NORMAL_COMPLETION);
+
+ bad2:
+ ipipe->intrxfer = NULL;
+ ipipe->repeat = 0;
+ usbd_free_xfer(xfer);
+ bad1:
+ usbd_close_pipe(ipipe);
+ return (err);
+}
+
+usbd_status
+usbd_close_pipe(usbd_pipe_handle pipe)
+{
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_close_pipe: pipe==NULL\n");
+ return (USBD_NORMAL_COMPLETION);
+ }
+#endif
+
+ if (--pipe->refcnt != 0)
+ return (USBD_NORMAL_COMPLETION);
+ if (! STAILQ_EMPTY(&pipe->queue))
+ return (USBD_PENDING_REQUESTS);
+ LIST_REMOVE(pipe, next);
+ pipe->endpoint->refcnt--;
+ pipe->methods->close(pipe);
+ if (pipe->intrxfer != NULL)
+ usbd_free_xfer(pipe->intrxfer);
+ free(pipe, M_USB);
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_transfer(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct usb_dma_mapping *dmap = &xfer->dmamap;
+ usbd_status err;
+ u_int size;
+ int s;
+
+ DPRINTFN(5,("%s: xfer=%p, flags=0x%b, rqflags=0x%b, "
+ "length=%d, buffer=%p, allocbuf=%p, pipe=%p, running=%d\n",
+ __func__, xfer, xfer->flags, USBD_BITS, xfer->rqflags, URQ_BITS,
+ xfer->length, xfer->buffer, xfer->allocbuf, pipe, pipe->running));
+#ifdef USB_DEBUG
+ if (usbdebug > 5)
+ usbd_dump_queue(pipe);
+#endif
+ xfer->done = 0;
+
+ if (pipe->aborting)
+ return (USBD_CANCELLED);
+
+ size = xfer->length;
+ /* If there is no buffer, allocate one. */
+ if (!(xfer->rqflags & URQ_DEV_DMABUF) && size != 0) {
+ bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag;
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & URQ_AUTO_DMABUF)
+ printf("usbd_transfer: has old buffer!\n");
+#endif
+ err = bus_dmamap_create(tag, 0, &dmap->map);
+ if (err)
+ return (USBD_NOMEM);
+
+ xfer->rqflags |= URQ_AUTO_DMABUF;
+ err = bus_dmamap_load(tag, dmap->map, xfer->buffer, size,
+ usbd_start_transfer, xfer, 0);
+ if (err != 0 && err != EINPROGRESS) {
+ xfer->rqflags &= ~URQ_AUTO_DMABUF;
+ bus_dmamap_destroy(tag, dmap->map);
+ return (USBD_INVAL);
+ }
+ } else if (size != 0) {
+ usbd_start_transfer(xfer, dmap->segs, dmap->nsegs, 0);
+ } else {
+ usbd_start_transfer(xfer, NULL, 0, 0);
+ }
+
+ if (!(xfer->flags & USBD_SYNCHRONOUS))
+ return (xfer->done ? USBD_NORMAL_COMPLETION : USBD_IN_PROGRESS);
+
+ /* Sync transfer, wait for completion. */
+ s = splusb();
+ while (!xfer->done) {
+ if (pipe->device->bus->use_polling)
+ panic("usbd_transfer: not done");
+ tsleep(xfer, PRIBIO, "usbsyn", 0);
+ }
+ splx(s);
+ return (xfer->status);
+}
+
+static void
+usbd_start_transfer(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ usbd_xfer_handle xfer = arg;
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct usb_dma_mapping *dmap = &xfer->dmamap;
+ bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag;
+ int err, i;
+
+ if (error != 0) {
+ KASSERT(xfer->rqflags & URQ_AUTO_DMABUF,
+ ("usbd_start_transfer: error with non-auto buf"));
+ if (nseg > 0)
+ bus_dmamap_unload(tag, dmap->map);
+ bus_dmamap_destroy(tag, dmap->map);
+ /* XXX */
+ usb_insert_transfer(xfer);
+ xfer->status = USBD_IOERROR;
+ usb_transfer_complete(xfer);
+ return;
+ }
+
+ if (segs != dmap->segs) {
+ for (i = 0; i < nseg; i++)
+ dmap->segs[i] = segs[i];
+ }
+ dmap->nsegs = nseg;
+
+ if (nseg > 0) {
+ if (!usbd_xfer_isread(xfer)) {
+ /*
+ * Copy data if it is not already in the correct
+ * buffer.
+ */
+ if (!(xfer->flags & USBD_NO_COPY) &&
+ xfer->allocbuf != NULL &&
+ xfer->buffer != xfer->allocbuf)
+ memcpy(xfer->allocbuf, xfer->buffer,
+ xfer->length);
+ bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREWRITE);
+ } else if (xfer->rqflags & URQ_REQUEST) {
+ /*
+ * Even if we have no data portion we still need to
+ * sync the dmamap for the request data in the SETUP
+ * packet.
+ */
+ bus_dmamap_sync(tag, dmap->map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ } else
+ bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREREAD);
+ }
+ err = pipe->methods->transfer(xfer);
+ if (err != USBD_IN_PROGRESS && err) {
+ if (xfer->rqflags & URQ_AUTO_DMABUF) {
+ bus_dmamap_unload(tag, dmap->map);
+ bus_dmamap_destroy(tag, dmap->map);
+ xfer->rqflags &= ~URQ_AUTO_DMABUF;
+ }
+ xfer->status = err;
+ usb_transfer_complete(xfer);
+ return;
+ }
+}
+
+/* Like usbd_transfer(), but waits for completion. */
+usbd_status
+usbd_sync_transfer(usbd_xfer_handle xfer)
+{
+ xfer->flags |= USBD_SYNCHRONOUS;
+ return (usbd_transfer(xfer));
+}
+
+struct usbd_allocstate {
+ usbd_xfer_handle xfer;
+ int done;
+ int error;
+ int waiting;
+};
+
+void *
+usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size)
+{
+ struct usbd_allocstate allocstate;
+ struct usb_dma_mapping *dmap = &xfer->dmamap;
+ bus_dma_tag_t tag = xfer->device->bus->buffer_dmatag;
+ void *buf;
+ usbd_status err;
+ int error, s;
+
+ KASSERT((xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) == 0,
+ ("usbd_alloc_buffer: xfer already has a buffer"));
+ err = bus_dmamap_create(tag, 0, &dmap->map);
+ if (err)
+ return (NULL);
+ buf = malloc(size, M_USB, M_WAITOK);
+
+ allocstate.xfer = xfer;
+ allocstate.done = 0;
+ allocstate.error = 0;
+ allocstate.waiting = 0;
+ error = bus_dmamap_load(tag, dmap->map, buf, size, usbd_alloc_callback,
+ &allocstate, 0);
+ if (error && error != EINPROGRESS) {
+ bus_dmamap_destroy(tag, dmap->map);
+ free(buf, M_USB);
+ return (NULL);
+ }
+ if (error == EINPROGRESS) {
+ /* Wait for completion. */
+ s = splusb();
+ allocstate.waiting = 1;
+ while (!allocstate.done)
+ tsleep(&allocstate, PRIBIO, "usbdab", 0);
+ splx(s);
+ error = allocstate.error;
+ }
+ if (error) {
+ bus_dmamap_unload(tag, dmap->map);
+ bus_dmamap_destroy(tag, dmap->map);
+ free(buf, M_USB);
+ return (NULL);
+ }
+
+ xfer->allocbuf = buf;
+ xfer->rqflags |= URQ_DEV_DMABUF;
+ return (buf);
+}
+
+void
+usbd_free_buffer(usbd_xfer_handle xfer)
+{
+ struct usb_dma_mapping *dmap = &xfer->dmamap;
+ bus_dma_tag_t tag = xfer->device->bus->buffer_dmatag;
+
+ KASSERT((xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) ==
+ URQ_DEV_DMABUF, ("usbd_free_buffer: no/auto buffer"));
+
+ xfer->rqflags &= ~URQ_DEV_DMABUF;
+ bus_dmamap_unload(tag, dmap->map);
+ bus_dmamap_destroy(tag, dmap->map);
+ free(xfer->allocbuf, M_USB);
+ xfer->allocbuf = NULL;
+}
+
+void *
+usbd_get_buffer(usbd_xfer_handle xfer)
+{
+ if (!(xfer->rqflags & URQ_DEV_DMABUF))
+ return (NULL);
+ return (xfer->allocbuf);
+}
+
+static void
+usbd_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct usbd_allocstate *allocstate = arg;
+ usbd_xfer_handle xfer = allocstate->xfer;
+ struct usb_dma_mapping *dmap = &xfer->dmamap;
+ int i;
+
+ allocstate->error = error;
+ if (error == 0) {
+ for (i = 0; i < nseg; i++)
+ dmap->segs[i] = segs[i];
+ dmap->nsegs = nseg;
+ }
+ allocstate->done = 1;
+ if (allocstate->waiting)
+ wakeup(&allocstate);
+}
+
+usbd_xfer_handle
+usbd_alloc_xfer(usbd_device_handle dev)
+{
+ usbd_xfer_handle xfer;
+
+ xfer = dev->bus->methods->allocx(dev->bus);
+ if (xfer == NULL)
+ return (NULL);
+ xfer->device = dev;
+ callout_init(&xfer->timeout_handle, 0);
+ DPRINTFN(5,("usbd_alloc_xfer() = %p\n", xfer));
+ return (xfer);
+}
+
+usbd_status
+usbd_free_xfer(usbd_xfer_handle xfer)
+{
+ DPRINTFN(5,("usbd_free_xfer: %p\n", xfer));
+ if (xfer->rqflags & URQ_DEV_DMABUF)
+ usbd_free_buffer(xfer);
+/* XXX Does FreeBSD need to do something similar? */
+#if defined(__NetBSD__) && defined(DIAGNOSTIC)
+ if (callout_pending(&xfer->timeout_handle)) {
+ callout_stop(&xfer->timeout_handle);
+ printf("usbd_free_xfer: timout_handle pending");
+ }
+#endif
+ xfer->device->bus->methods->freex(xfer->device->bus, xfer);
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usbd_setup_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ usbd_private_handle priv, void *buffer, u_int32_t length,
+ u_int16_t flags, u_int32_t timeout,
+ usbd_callback callback)
+{
+ xfer->pipe = pipe;
+ xfer->priv = priv;
+ xfer->buffer = buffer;
+ xfer->length = length;
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = timeout;
+ xfer->status = USBD_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->rqflags &= ~URQ_REQUEST;
+ xfer->nframes = 0;
+}
+
+void
+usbd_setup_default_xfer(usbd_xfer_handle xfer, usbd_device_handle dev,
+ usbd_private_handle priv, u_int32_t timeout,
+ usb_device_request_t *req, void *buffer,
+ u_int32_t length, u_int16_t flags,
+ usbd_callback callback)
+{
+ xfer->pipe = dev->default_pipe;
+ xfer->priv = priv;
+ xfer->buffer = buffer;
+ xfer->length = length;
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = timeout;
+ xfer->status = USBD_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->request = *req;
+ xfer->rqflags |= URQ_REQUEST;
+ xfer->nframes = 0;
+}
+
+void
+usbd_setup_isoc_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ usbd_private_handle priv, u_int16_t *frlengths,
+ u_int32_t nframes, u_int16_t flags, usbd_callback callback)
+{
+ int i;
+
+ xfer->pipe = pipe;
+ xfer->priv = priv;
+ xfer->buffer = 0;
+ xfer->length = 0;
+ for (i = 0; i < nframes; i++)
+ xfer->length += frlengths[i];
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = USBD_NO_TIMEOUT;
+ xfer->status = USBD_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->rqflags &= ~URQ_REQUEST;
+ xfer->frlengths = frlengths;
+ xfer->nframes = nframes;
+}
+
+void
+usbd_get_xfer_status(usbd_xfer_handle xfer, usbd_private_handle *priv,
+ void **buffer, u_int32_t *count, usbd_status *status)
+{
+ if (priv != NULL)
+ *priv = xfer->priv;
+ if (buffer != NULL)
+ *buffer = xfer->buffer;
+ if (count != NULL)
+ *count = xfer->actlen;
+ if (status != NULL)
+ *status = xfer->status;
+}
+
+usb_config_descriptor_t *
+usbd_get_config_descriptor(usbd_device_handle dev)
+{
+#ifdef DIAGNOSTIC
+ if (dev == NULL) {
+ printf("usbd_get_config_descriptor: dev == NULL\n");
+ return (NULL);
+ }
+#endif
+ return (dev->cdesc);
+}
+
+int
+usbd_get_speed(usbd_device_handle dev)
+{
+ return (dev->speed);
+}
+
+usb_interface_descriptor_t *
+usbd_get_interface_descriptor(usbd_interface_handle iface)
+{
+#ifdef DIAGNOSTIC
+ if (iface == NULL) {
+ printf("usbd_get_interface_descriptor: dev == NULL\n");
+ return (NULL);
+ }
+#endif
+ return (iface->idesc);
+}
+
+usb_device_descriptor_t *
+usbd_get_device_descriptor(usbd_device_handle dev)
+{
+ return (&dev->ddesc);
+}
+
+usb_endpoint_descriptor_t *
+usbd_interface2endpoint_descriptor(usbd_interface_handle iface, u_int8_t index)
+{
+ if (index >= iface->idesc->bNumEndpoints)
+ return (0);
+ return (iface->endpoints[index].edesc);
+}
+
+usbd_status
+usbd_abort_pipe(usbd_pipe_handle pipe)
+{
+ usbd_status err;
+ int s;
+
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_close_pipe: pipe==NULL\n");
+ return (USBD_NORMAL_COMPLETION);
+ }
+#endif
+ s = splusb();
+ err = usbd_ar_pipe(pipe);
+ splx(s);
+ return (err);
+}
+
+usbd_status
+usbd_abort_default_pipe(usbd_device_handle dev)
+{
+ return (usbd_abort_pipe(dev->default_pipe));
+}
+
+usbd_status
+usbd_clear_endpoint_stall(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTFN(8, ("usbd_clear_endpoint_stall\n"));
+
+ /*
+ * Clearing en endpoint stall resets the endpoint toggle, so
+ * do the same to the HC toggle.
+ */
+ pipe->methods->cleartoggle(pipe);
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(dev, &req, 0);
+#if 0
+XXX should we do this?
+ if (!err) {
+ pipe->state = USBD_PIPE_ACTIVE;
+ /* XXX activate pipe */
+ }
+#endif
+ return (err);
+}
+
+usbd_status
+usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe)
+{
+ usbd_device_handle dev = pipe->device;
+ usb_device_request_t req;
+ usbd_status err;
+
+ pipe->methods->cleartoggle(pipe);
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress);
+ USETW(req.wLength, 0);
+ err = usbd_do_request_async(dev, &req, 0);
+ return (err);
+}
+
+void
+usbd_clear_endpoint_toggle(usbd_pipe_handle pipe)
+{
+ pipe->methods->cleartoggle(pipe);
+}
+
+usbd_status
+usbd_endpoint_count(usbd_interface_handle iface, u_int8_t *count)
+{
+#ifdef DIAGNOSTIC
+ if (iface == NULL || iface->idesc == NULL) {
+ printf("usbd_endpoint_count: NULL pointer\n");
+ return (USBD_INVAL);
+ }
+#endif
+ *count = iface->idesc->bNumEndpoints;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_interface_count(usbd_device_handle dev, u_int8_t *count)
+{
+ if (dev->cdesc == NULL)
+ return (USBD_NOT_CONFIGURED);
+ *count = dev->cdesc->bNumInterface;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+usbd_interface2device_handle(usbd_interface_handle iface,
+ usbd_device_handle *dev)
+{
+ *dev = iface->device;
+}
+
+usbd_status
+usbd_device2interface_handle(usbd_device_handle dev,
+ u_int8_t ifaceno, usbd_interface_handle *iface)
+{
+ if (dev->cdesc == NULL)
+ return (USBD_NOT_CONFIGURED);
+ if (ifaceno >= dev->cdesc->bNumInterface)
+ return (USBD_INVAL);
+ *iface = &dev->ifaces[ifaceno];
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_device_handle
+usbd_pipe2device_handle(usbd_pipe_handle pipe)
+{
+ return (pipe->device);
+}
+
+/* XXXX use altno */
+usbd_status
+usbd_set_interface(usbd_interface_handle iface, int altidx)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ void *endpoints;
+
+ if (LIST_FIRST(&iface->pipes) != 0)
+ return (USBD_IN_USE);
+
+ endpoints = iface->endpoints;
+ err = usbd_fill_iface_data(iface->device, iface->index, altidx);
+ if (err)
+ return (err);
+
+ /* new setting works, we can free old endpoints */
+ if (endpoints != NULL)
+ free(endpoints, M_USB);
+
+#ifdef DIAGNOSTIC
+ if (iface->idesc == NULL) {
+ printf("usbd_set_interface: NULL pointer\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ req.bmRequestType = UT_WRITE_INTERFACE;
+ req.bRequest = UR_SET_INTERFACE;
+ USETW(req.wValue, iface->idesc->bAlternateSetting);
+ USETW(req.wIndex, iface->idesc->bInterfaceNumber);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(iface->device, &req, 0));
+}
+
+int
+usbd_get_no_alts(usb_config_descriptor_t *cdesc, int ifaceno)
+{
+ char *p = (char *)cdesc;
+ char *end = p + UGETW(cdesc->wTotalLength);
+ usb_interface_descriptor_t *d;
+ int n;
+
+ for (n = 0; p < end; p += d->bLength) {
+ d = (usb_interface_descriptor_t *)p;
+ if (p + d->bLength <= end &&
+ d->bDescriptorType == UDESC_INTERFACE &&
+ d->bInterfaceNumber == ifaceno)
+ n++;
+ }
+ return (n);
+}
+
+int
+usbd_get_interface_altindex(usbd_interface_handle iface)
+{
+ return (iface->altindex);
+}
+
+usbd_status
+usbd_get_interface(usbd_interface_handle iface, u_int8_t *aiface)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_INTERFACE;
+ req.bRequest = UR_GET_INTERFACE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, iface->idesc->bInterfaceNumber);
+ USETW(req.wLength, 1);
+ return (usbd_do_request(iface->device, &req, aiface));
+}
+
+/*** Internal routines ***/
+
+/* Dequeue all pipe operations, called at splusb(). */
+static usbd_status
+usbd_ar_pipe(usbd_pipe_handle pipe)
+{
+ usbd_xfer_handle xfer;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(2,("usbd_ar_pipe: pipe=%p\n", pipe));
+#ifdef USB_DEBUG
+ if (usbdebug > 5)
+ usbd_dump_queue(pipe);
+#endif
+ pipe->repeat = 0;
+ pipe->aborting = 1;
+ while ((xfer = STAILQ_FIRST(&pipe->queue)) != NULL) {
+ DPRINTFN(2,("usbd_ar_pipe: pipe=%p xfer=%p (methods=%p)\n",
+ pipe, xfer, pipe->methods));
+ /* Make the HC abort it (and invoke the callback). */
+ pipe->methods->abort(xfer);
+ KASSERT(STAILQ_FIRST(&pipe->queue) != xfer, ("usbd_ar_pipe"));
+ /* XXX only for non-0 usbd_clear_endpoint_stall(pipe); */
+ }
+ pipe->aborting = 0;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+/* Called at splusb() */
+void
+usb_transfer_complete(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ struct usb_dma_mapping *dmap = &xfer->dmamap;
+ bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag;
+ int sync = xfer->flags & USBD_SYNCHRONOUS;
+ int erred = xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_TIMEOUT;
+ int repeat = pipe->repeat;
+ int polling;
+
+ SPLUSBCHECK;
+
+ DPRINTFN(5, ("%s: pipe=%p xfer=%p status=%d actlen=%d\n",
+ __func__, pipe, xfer, xfer->status, xfer->actlen));
+ DPRINTFN(5,("%s: flags=0x%b, rqflags=0x%b, length=%d, buffer=%p\n",
+ __func__, xfer->flags, USBD_BITS, xfer->rqflags, URQ_BITS,
+ xfer->length, xfer->buffer));
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_ONQU) {
+ printf("usb_transfer_complete: xfer=%p not busy 0x%08x\n",
+ xfer, xfer->busy_free);
+ return;
+ }
+#endif
+
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_transfer_cb: pipe==0, xfer=%p\n", xfer);
+ return;
+ }
+#endif
+ polling = pipe->device->bus->use_polling;
+ /* XXXX */
+ if (polling)
+ pipe->running = 0;
+
+ if (xfer->actlen != 0 && usbd_xfer_isread(xfer)) {
+ bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_POSTREAD);
+ /* Copy data if it is not already in the correct buffer. */
+ if (!(xfer->flags & USBD_NO_COPY) && xfer->allocbuf != NULL &&
+ xfer->buffer != xfer->allocbuf)
+ memcpy(xfer->buffer, xfer->allocbuf, xfer->actlen);
+ }
+
+ /* if we mapped the buffer in usbd_transfer() we unmap it here. */
+ if (xfer->rqflags & URQ_AUTO_DMABUF) {
+ if (!repeat) {
+ bus_dmamap_unload(tag, dmap->map);
+ bus_dmamap_destroy(tag, dmap->map);
+ xfer->rqflags &= ~URQ_AUTO_DMABUF;
+ }
+ }
+
+ if (!repeat) {
+ /* Remove request from queue. */
+#ifdef DIAGNOSTIC
+ xfer->busy_free = XFER_BUSY;
+#endif
+ KASSERT(STAILQ_FIRST(&pipe->queue) == xfer,
+ ("usb_transfer_complete: bad dequeue"));
+ STAILQ_REMOVE_HEAD(&pipe->queue, next);
+ }
+ DPRINTFN(5,("usb_transfer_complete: repeat=%d new head=%p\n",
+ repeat, STAILQ_FIRST(&pipe->queue)));
+
+ /* Count completed transfers. */
+ ++pipe->device->bus->stats.uds_requests
+ [pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE];
+
+ xfer->done = 1;
+ if (!xfer->status && xfer->actlen < xfer->length &&
+ !(xfer->flags & USBD_SHORT_XFER_OK)) {
+ DPRINTFN(-1,("usbd_transfer_cb: short transfer %d<%d\n",
+ xfer->actlen, xfer->length));
+ xfer->status = USBD_SHORT_XFER;
+ }
+
+ /*
+ * For repeat operations, call the callback first, as the xfer
+ * will not go away and the "done" method may modify it. Otherwise
+ * reverse the order in case the callback wants to free or reuse
+ * the xfer.
+ */
+ if (repeat) {
+ if (xfer->callback)
+ xfer->callback(xfer, xfer->priv, xfer->status);
+ pipe->methods->done(xfer);
+ } else {
+ pipe->methods->done(xfer);
+ if (xfer->callback)
+ xfer->callback(xfer, xfer->priv, xfer->status);
+ }
+
+ if (sync && !polling)
+ wakeup(xfer);
+
+ if (!repeat) {
+ /* XXX should we stop the queue on all errors? */
+ if (erred && pipe->iface != NULL) /* not control pipe */
+ pipe->running = 0;
+ else
+ usbd_start_next(pipe);
+ }
+}
+
+usbd_status
+usb_insert_transfer(usbd_xfer_handle xfer)
+{
+ usbd_pipe_handle pipe = xfer->pipe;
+ usbd_status err;
+ int s;
+
+ DPRINTFN(5,("usb_insert_transfer: pipe=%p running=%d timeout=%d\n",
+ pipe, pipe->running, xfer->timeout));
+#ifdef DIAGNOSTIC
+ if (xfer->busy_free != XFER_BUSY) {
+ printf("usb_insert_transfer: xfer=%p not busy 0x%08x\n",
+ xfer, xfer->busy_free);
+ return (USBD_INVAL);
+ }
+ xfer->busy_free = XFER_ONQU;
+#endif
+ s = splusb();
+ KASSERT(STAILQ_FIRST(&pipe->queue) != xfer, ("usb_insert_transfer"));
+ STAILQ_INSERT_TAIL(&pipe->queue, xfer, next);
+ if (pipe->running)
+ err = USBD_IN_PROGRESS;
+ else {
+ pipe->running = 1;
+ err = USBD_NORMAL_COMPLETION;
+ }
+ splx(s);
+ return (err);
+}
+
+/* Called at splusb() */
+void
+usbd_start_next(usbd_pipe_handle pipe)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ SPLUSBCHECK;
+
+#ifdef DIAGNOSTIC
+ if (pipe == NULL) {
+ printf("usbd_start_next: pipe == NULL\n");
+ return;
+ }
+ if (pipe->methods == NULL || pipe->methods->start == NULL) {
+ printf("usbd_start_next: pipe=%p no start method\n", pipe);
+ return;
+ }
+#endif
+
+ /* Get next request in queue. */
+ xfer = STAILQ_FIRST(&pipe->queue);
+ DPRINTFN(5, ("usbd_start_next: pipe=%p, xfer=%p\n", pipe, xfer));
+ if (xfer == NULL) {
+ pipe->running = 0;
+ } else {
+ err = pipe->methods->start(xfer);
+ if (err != USBD_IN_PROGRESS) {
+ printf("usbd_start_next: error=%d\n", err);
+ pipe->running = 0;
+ /* XXX do what? */
+ }
+ }
+}
+
+usbd_status
+usbd_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data)
+{
+ return (usbd_do_request_flags(dev, req, data, 0, 0,
+ USBD_DEFAULT_TIMEOUT));
+}
+
+usbd_status
+usbd_do_request_flags(usbd_device_handle dev, usb_device_request_t *req,
+ void *data, u_int16_t flags, int *actlen, u_int32_t timo)
+{
+ return (usbd_do_request_flags_pipe(dev, dev->default_pipe, req,
+ data, flags, actlen, timo));
+}
+
+usbd_status
+usbd_do_request_flags_pipe(usbd_device_handle dev, usbd_pipe_handle pipe,
+ usb_device_request_t *req, void *data, u_int16_t flags, int *actlen,
+ u_int32_t timeout)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+#ifdef DIAGNOSTIC
+/* XXX amd64 too? */
+#if defined(__i386__)
+ KASSERT(curthread->td_intr_nesting_level == 0,
+ ("usbd_do_request: in interrupt context"));
+#endif
+ if (dev->bus->intr_context) {
+ printf("usbd_do_request: not in process context\n");
+ return (USBD_INVAL);
+ }
+#endif
+
+ xfer = usbd_alloc_xfer(dev);
+ if (xfer == NULL)
+ return (USBD_NOMEM);
+ usbd_setup_default_xfer(xfer, dev, 0, timeout, req,
+ data, UGETW(req->wLength), flags, 0);
+ xfer->pipe = pipe;
+ err = usbd_sync_transfer(xfer);
+#if defined(USB_DEBUG) || defined(DIAGNOSTIC)
+ if (xfer->actlen > xfer->length)
+ DPRINTF(("usbd_do_request: overrun addr=%d type=0x%02x req=0x"
+ "%02x val=%d index=%d rlen=%d length=%d actlen=%d\n",
+ dev->address, xfer->request.bmRequestType,
+ xfer->request.bRequest, UGETW(xfer->request.wValue),
+ UGETW(xfer->request.wIndex),
+ UGETW(xfer->request.wLength),
+ xfer->length, xfer->actlen));
+#endif
+ if (actlen != NULL)
+ *actlen = xfer->actlen;
+ if (err == USBD_STALLED) {
+ /*
+ * The control endpoint has stalled. Control endpoints
+ * should not halt, but some may do so anyway so clear
+ * any halt condition.
+ */
+ usb_device_request_t treq;
+ usb_status_t status;
+ u_int16_t s;
+ usbd_status nerr;
+
+ treq.bmRequestType = UT_READ_ENDPOINT;
+ treq.bRequest = UR_GET_STATUS;
+ USETW(treq.wValue, 0);
+ USETW(treq.wIndex, 0);
+ USETW(treq.wLength, sizeof(usb_status_t));
+ usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT,
+ &treq, &status,sizeof(usb_status_t),
+ 0, 0);
+ nerr = usbd_sync_transfer(xfer);
+ if (nerr)
+ goto bad;
+ s = UGETW(status.wStatus);
+ DPRINTF(("usbd_do_request: status = 0x%04x\n", s));
+ if (!(s & UES_HALT))
+ goto bad;
+ treq.bmRequestType = UT_WRITE_ENDPOINT;
+ treq.bRequest = UR_CLEAR_FEATURE;
+ USETW(treq.wValue, UF_ENDPOINT_HALT);
+ USETW(treq.wIndex, 0);
+ USETW(treq.wLength, 0);
+ usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT,
+ &treq, &status, 0, 0, 0);
+ nerr = usbd_sync_transfer(xfer);
+ if (nerr)
+ goto bad;
+ }
+
+ bad:
+ usbd_free_xfer(xfer);
+ return (err);
+}
+
+void
+usbd_do_request_async_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+#if defined(USB_DEBUG) || defined(DIAGNOSTIC)
+ if (xfer->actlen > xfer->length)
+ DPRINTF(("usbd_do_request: overrun addr=%d type=0x%02x req=0x"
+ "%02x val=%d index=%d rlen=%d length=%d actlen=%d\n",
+ xfer->pipe->device->address,
+ xfer->request.bmRequestType,
+ xfer->request.bRequest, UGETW(xfer->request.wValue),
+ UGETW(xfer->request.wIndex),
+ UGETW(xfer->request.wLength),
+ xfer->length, xfer->actlen));
+#endif
+ usbd_free_xfer(xfer);
+}
+
+/*
+ * Execute a request without waiting for completion.
+ * Can be used from interrupt context.
+ */
+usbd_status
+usbd_do_request_async(usbd_device_handle dev, usb_device_request_t *req,
+ void *data)
+{
+ usbd_xfer_handle xfer;
+ usbd_status err;
+
+ xfer = usbd_alloc_xfer(dev);
+ if (xfer == NULL)
+ return (USBD_NOMEM);
+ usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT, req,
+ data, UGETW(req->wLength), 0, usbd_do_request_async_cb);
+ err = usbd_transfer(xfer);
+ if (err != USBD_IN_PROGRESS && err) {
+ usbd_free_xfer(xfer);
+ return (err);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+const struct usbd_quirks *
+usbd_get_quirks(usbd_device_handle dev)
+{
+#ifdef DIAGNOSTIC
+ if (dev == NULL) {
+ printf("usbd_get_quirks: dev == NULL\n");
+ return 0;
+ }
+#endif
+ return (dev->quirks);
+}
+
+/* XXX do periodic free() of free list */
+
+/*
+ * Called from keyboard driver when in polling mode.
+ */
+void
+usbd_dopoll(usbd_interface_handle iface)
+{
+ iface->device->bus->methods->do_poll(iface->device->bus);
+}
+
+void
+usbd_set_polling(usbd_device_handle dev, int on)
+{
+ if (on)
+ dev->bus->use_polling++;
+ else
+ dev->bus->use_polling--;
+ /* When polling we need to make sure there is nothing pending to do. */
+ if (dev->bus->use_polling)
+ dev->bus->methods->soft_intr(dev->bus);
+}
+
+usbd_status
+usbd_reset_device(usbd_device_handle dev)
+{
+ usbd_device_handle parent = dev->myhub;
+ struct usbd_port *up = dev->powersrc;
+
+ return usbd_reset_port(parent, up->portno, &up->status);
+}
+
+
+usb_endpoint_descriptor_t *
+usbd_get_endpoint_descriptor(usbd_interface_handle iface, u_int8_t address)
+{
+ struct usbd_endpoint *ep;
+ int i;
+
+ for (i = 0; i < iface->idesc->bNumEndpoints; i++) {
+ ep = &iface->endpoints[i];
+ if (ep->edesc->bEndpointAddress == address)
+ return (iface->endpoints[i].edesc);
+ }
+ return (0);
+}
+
+/*
+ * usbd_ratecheck() can limit the number of error messages that occurs.
+ * When a device is unplugged it may take up to 0.25s for the hub driver
+ * to notice it. If the driver continuosly tries to do I/O operations
+ * this can generate a large number of messages.
+ */
+int
+usbd_ratecheck(struct timeval *last)
+{
+ if (last->tv_sec == time_second)
+ return (0);
+ last->tv_sec = time_second;
+ return (1);
+}
+
+/*
+ * Search for a vendor/product pair in an array. The item size is
+ * given as an argument.
+ */
+const struct usb_devno *
+usb_match_device(const struct usb_devno *tbl, u_int nentries, u_int sz,
+ u_int16_t vendor, u_int16_t product)
+{
+ while (nentries-- > 0) {
+ u_int16_t tproduct = tbl->ud_product;
+ if (tbl->ud_vendor == vendor &&
+ (tproduct == product || tproduct == USB_PRODUCT_ANY))
+ return (tbl);
+ tbl = (const struct usb_devno *)((const char *)tbl + sz);
+ }
+ return (NULL);
+}
+
+
+void
+usb_desc_iter_init(usbd_device_handle dev, usbd_desc_iter_t *iter)
+{
+ const usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
+
+ iter->cur = (const uByte *)cd;
+ iter->end = (const uByte *)cd + UGETW(cd->wTotalLength);
+}
+
+const usb_descriptor_t *
+usb_desc_iter_next(usbd_desc_iter_t *iter)
+{
+ const usb_descriptor_t *desc;
+
+ if (iter->cur + sizeof(usb_descriptor_t) >= iter->end) {
+ if (iter->cur != iter->end)
+ printf("usb_desc_iter_next: bad descriptor\n");
+ return NULL;
+ }
+ desc = (const usb_descriptor_t *)iter->cur;
+ if (desc->bLength == 0) {
+ printf("usb_desc_iter_next: descriptor length = 0\n");
+ return NULL;
+ }
+ iter->cur += desc->bLength;
+ if (iter->cur > iter->end) {
+ printf("usb_desc_iter_next: descriptor length too large\n");
+ return NULL;
+ }
+ return desc;
+}
+
+usbd_status
+usbd_get_string(usbd_device_handle dev, int si, char *buf, size_t len)
+{
+ int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;
+ usb_string_descriptor_t us;
+ char *s;
+ int i, n;
+ u_int16_t c;
+ usbd_status err;
+ int size;
+
+ buf[0] = '\0';
+ if (len == 0)
+ return (USBD_NORMAL_COMPLETION);
+ if (si == 0)
+ return (USBD_INVAL);
+ if (dev->quirks->uq_flags & UQ_NO_STRINGS)
+ return (USBD_STALLED);
+ if (dev->langid == USBD_NOLANG) {
+ /* Set up default language */
+ err = usbd_get_string_desc(dev, USB_LANGUAGE_TABLE, 0, &us,
+ &size);
+ if (err || size < 4) {
+ DPRINTFN(-1,("usbd_get_string: getting lang failed, using 0\n"));
+ dev->langid = 0; /* Well, just pick something then */
+ } else {
+ /* Pick the first language as the default. */
+ dev->langid = UGETW(us.bString[0]);
+ }
+ }
+ err = usbd_get_string_desc(dev, si, dev->langid, &us, &size);
+ if (err)
+ return (err);
+ s = buf;
+ n = size / 2 - 1;
+ for (i = 0; i < n && i < len - 1; i++) {
+ c = UGETW(us.bString[i]);
+ /* Convert from Unicode, handle buggy strings. */
+ if ((c & 0xff00) == 0)
+ *s++ = c;
+ else if ((c & 0x00ff) == 0 && swap)
+ *s++ = c >> 8;
+ else
+ *s++ = '?';
+ }
+ *s++ = 0;
+ return (USBD_NORMAL_COMPLETION);
+}
+
+int
+usbd_driver_load(module_t mod, int what, void *arg)
+{
+ /* XXX should implement something like a function that removes all generic devices */
+
+ return (0);
+}
diff --git a/sys/legacy/dev/usb/usbdi.h b/sys/legacy/dev/usb/usbdi.h
new file mode 100644
index 0000000..ef59039
--- /dev/null
+++ b/sys/legacy/dev/usb/usbdi.h
@@ -0,0 +1,289 @@
+/* $NetBSD: usbdi.h,v 1.64 2004/10/23 13:26:34 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+#ifndef _USBDI_H_
+#define _USBDI_H_
+
+typedef struct usbd_bus *usbd_bus_handle;
+typedef struct usbd_device *usbd_device_handle;
+typedef struct usbd_interface *usbd_interface_handle;
+typedef struct usbd_pipe *usbd_pipe_handle;
+typedef struct usbd_xfer *usbd_xfer_handle;
+typedef void *usbd_private_handle;
+
+typedef enum { /* keep in sync with usbd_status_msgs */
+ USBD_NORMAL_COMPLETION = 0, /* must be 0 */
+ USBD_IN_PROGRESS, /* 1 */
+ /* errors */
+ USBD_PENDING_REQUESTS, /* 2 */
+ USBD_NOT_STARTED, /* 3 */
+ USBD_INVAL, /* 4 */
+ USBD_NOMEM, /* 5 */
+ USBD_CANCELLED, /* 6 */
+ USBD_BAD_ADDRESS, /* 7 */
+ USBD_IN_USE, /* 8 */
+ USBD_NO_ADDR, /* 9 */
+ USBD_SET_ADDR_FAILED, /* 10 */
+ USBD_NO_POWER, /* 11 */
+ USBD_TOO_DEEP, /* 12 */
+ USBD_IOERROR, /* 13 */
+ USBD_NOT_CONFIGURED, /* 14 */
+ USBD_TIMEOUT, /* 15 */
+ USBD_SHORT_XFER, /* 16 */
+ USBD_STALLED, /* 17 */
+ USBD_INTERRUPTED, /* 18 */
+
+ USBD_ERROR_MAX /* must be last */
+} usbd_status;
+
+typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+
+/* Open flags */
+#define USBD_EXCLUSIVE_USE 0x01
+
+/* Use default (specified by ep. desc.) interval on interrupt pipe */
+#define USBD_DEFAULT_INTERVAL (-1)
+
+/* Request flags */
+#define USBD_NO_COPY 0x01 /* do not copy data to DMA buffer */
+#define USBD_SYNCHRONOUS 0x02 /* wait for completion */
+/* in usb.h #define USBD_SHORT_XFER_OK 0x04*/ /* allow short reads */
+#define USBD_FORCE_SHORT_XFER 0x08 /* force last short packet on write */
+
+#define USBD_BITS "\20\1NO_COPY\2SYNCHRONOUS\4FORCE_SHORT_XFER"
+
+#define USBD_NO_TIMEOUT 0
+#define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */
+
+usbd_status usbd_open_pipe(usbd_interface_handle, u_int8_t,
+ u_int8_t, usbd_pipe_handle *);
+usbd_status usbd_close_pipe(usbd_pipe_handle);
+usbd_status usbd_transfer(usbd_xfer_handle);
+usbd_xfer_handle usbd_alloc_xfer(usbd_device_handle);
+usbd_status usbd_free_xfer(usbd_xfer_handle);
+void usbd_setup_xfer(usbd_xfer_handle, usbd_pipe_handle,
+ usbd_private_handle, void *,
+ u_int32_t, u_int16_t, u_int32_t,
+ usbd_callback);
+void usbd_setup_default_xfer(usbd_xfer_handle, usbd_device_handle,
+ usbd_private_handle, u_int32_t,
+ usb_device_request_t *, void *,
+ u_int32_t, u_int16_t, usbd_callback);
+void usbd_setup_isoc_xfer(usbd_xfer_handle, usbd_pipe_handle,
+ usbd_private_handle, u_int16_t *,
+ u_int32_t, u_int16_t, usbd_callback);
+void usbd_get_xfer_status(usbd_xfer_handle, usbd_private_handle *,
+ void **, u_int32_t *, usbd_status *);
+usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor
+ (usbd_interface_handle, u_int8_t);
+usbd_status usbd_abort_pipe(usbd_pipe_handle);
+usbd_status usbd_abort_default_pipe(usbd_device_handle);
+usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle);
+usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle);
+void usbd_clear_endpoint_toggle(usbd_pipe_handle);
+usbd_status usbd_endpoint_count(usbd_interface_handle, u_int8_t *);
+usbd_status usbd_interface_count(usbd_device_handle, u_int8_t *);
+void usbd_interface2device_handle(usbd_interface_handle,
+ usbd_device_handle *);
+usbd_status usbd_device2interface_handle(usbd_device_handle,
+ u_int8_t, usbd_interface_handle *);
+
+usbd_device_handle usbd_pipe2device_handle(usbd_pipe_handle);
+void *usbd_alloc_buffer(usbd_xfer_handle, u_int32_t);
+void usbd_free_buffer(usbd_xfer_handle);
+void *usbd_get_buffer(usbd_xfer_handle);
+usbd_status usbd_sync_transfer(usbd_xfer_handle);
+usbd_status usbd_open_pipe_intr(usbd_interface_handle, u_int8_t,
+ u_int8_t, usbd_pipe_handle *,
+ usbd_private_handle, void *,
+ u_int32_t, usbd_callback, int);
+usbd_status usbd_do_request(usbd_device_handle, usb_device_request_t *, void *);
+usbd_status usbd_do_request_async(usbd_device_handle,
+ usb_device_request_t *, void *);
+usbd_status usbd_do_request_flags(usbd_device_handle, usb_device_request_t *,
+ void *, u_int16_t, int*, u_int32_t);
+usbd_status usbd_do_request_flags_pipe(usbd_device_handle, usbd_pipe_handle,
+ usb_device_request_t *, void *, u_int16_t, int *, u_int32_t);
+usb_interface_descriptor_t *usbd_get_interface_descriptor
+ (usbd_interface_handle);
+usb_config_descriptor_t *usbd_get_config_descriptor(usbd_device_handle);
+usb_device_descriptor_t *usbd_get_device_descriptor(usbd_device_handle);
+int usbd_get_speed(usbd_device_handle);
+usbd_status usbd_set_interface(usbd_interface_handle, int);
+int usbd_get_no_alts(usb_config_descriptor_t *, int);
+usbd_status usbd_get_interface(usbd_interface_handle, u_int8_t *);
+void usbd_fill_deviceinfo(usbd_device_handle, struct usb_device_info *, int);
+int usbd_get_interface_altindex(usbd_interface_handle);
+
+usb_interface_descriptor_t *usbd_find_idesc(usb_config_descriptor_t *,
+ int, int);
+usb_endpoint_descriptor_t *usbd_find_edesc(usb_config_descriptor_t *,
+ int, int, int);
+
+void usbd_dopoll(usbd_interface_handle);
+void usbd_set_polling(usbd_device_handle, int);
+usbd_status usbd_reset_device(usbd_device_handle);
+
+const char *usbd_errstr(usbd_status);
+
+void usbd_add_dev_event(int, usbd_device_handle);
+void usbd_add_drv_event(int, usbd_device_handle, device_t);
+
+void usbd_devinfo(usbd_device_handle, int, char *);
+const struct usbd_quirks *usbd_get_quirks(usbd_device_handle);
+usb_endpoint_descriptor_t *usbd_get_endpoint_descriptor
+ (usbd_interface_handle, u_int8_t);
+
+usbd_status usbd_reload_device_desc(usbd_device_handle);
+
+int usbd_ratecheck(struct timeval *last);
+
+usbd_status usbd_get_string(usbd_device_handle dev, int si, char *buf,
+ size_t len);
+
+/* An iterator for descriptors. */
+typedef struct {
+ const uByte *cur;
+ const uByte *end;
+} usbd_desc_iter_t;
+void usb_desc_iter_init(usbd_device_handle dev, usbd_desc_iter_t *iter);
+const usb_descriptor_t *usb_desc_iter_next(usbd_desc_iter_t *iter);
+
+/*
+ * The usb_task structs form a queue of things to run in the USB event
+ * thread. Normally this is just device discovery when a connect/disconnect
+ * has been detected. But it may also be used by drivers that need to
+ * perform (short) tasks that must have a process context.
+ */
+struct usb_task {
+ TAILQ_ENTRY(usb_task) next;
+ void (*fun)(void *);
+ void *arg;
+ int queue;
+};
+#define USB_TASKQ_HC 0
+#define USB_TASKQ_DRIVER 1
+#define USB_NUM_TASKQS 2
+#define USB_TASKQ_NAMES {"usbtask-hc", "usbtask-dr"}
+
+void usb_add_task(usbd_device_handle, struct usb_task *, int queue);
+void usb_rem_task(usbd_device_handle, struct usb_task *);
+#define usb_init_task(t, f, a) ((t)->fun = (f), (t)->arg = (a), (t)->queue = -1)
+
+struct usb_devno {
+ u_int16_t ud_vendor;
+ u_int16_t ud_product;
+};
+const struct usb_devno *usb_match_device(const struct usb_devno *,
+ u_int, u_int, u_int16_t, u_int16_t);
+#define usb_lookup(tbl, vendor, product) \
+ usb_match_device((const struct usb_devno *)(tbl), sizeof (tbl) / sizeof ((tbl)[0]), sizeof ((tbl)[0]), (vendor), (product))
+#define USB_PRODUCT_ANY 0xffff
+
+/* NetBSD attachment information */
+
+/* Attach data */
+struct usb_attach_arg {
+ int port;
+ int configno;
+ int ifaceno;
+ int vendor;
+ int product;
+ int release;
+ int matchlvl;
+ usbd_device_handle device; /* current device */
+ usbd_interface_handle iface; /* current interface */
+ int usegeneric;
+ usbd_interface_handle *ifaces; /* all interfaces */
+ int nifaces; /* number of interfaces */
+};
+
+/* FreeBSD needs values less than zero */
+#define UMATCH_VENDOR_PRODUCT_REV (-10)
+#define UMATCH_VENDOR_PRODUCT (-20)
+#define UMATCH_VENDOR_DEVCLASS_DEVPROTO (-30)
+#define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO (-40)
+#define UMATCH_DEVCLASS_DEVSUBCLASS (-50)
+#define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE (-60)
+#define UMATCH_VENDOR_PRODUCT_CONF_IFACE (-70)
+#define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO (-80)
+#define UMATCH_VENDOR_IFACESUBCLASS (-90)
+#define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO (-100)
+#define UMATCH_IFACECLASS_IFACESUBCLASS (-110)
+#define UMATCH_IFACECLASS (-120)
+#define UMATCH_IFACECLASS_GENERIC (-130)
+#define UMATCH_GENERIC (-140)
+#define UMATCH_NONE (ENXIO)
+
+#define USBD_SHOW_DEVICE_CLASS 0x1
+#define USBD_SHOW_INTERFACE_CLASS 0x2
+
+struct module;
+int usbd_driver_load(struct module *mod, int what, void *arg);
+
+static inline int
+usb_get_port(device_t dev)
+{
+ struct usb_attach_arg *uap = device_get_ivars(dev);
+ return (uap->port);
+}
+
+static inline struct usbd_interface *
+usb_get_iface(device_t dev)
+{
+ struct usb_attach_arg *uap = device_get_ivars(dev);
+ return (uap->iface);
+}
+
+/* XXX Perhaps USB should have its own levels? */
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+#define splusb splsoftnet
+#else
+#define splusb splsoftclock
+#endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */
+#else
+#define splusb splbio
+#endif /* USB_USE_SOFTINTR */
+#define splhardusb splbio
+#define IPL_USB IPL_BIO
+
+#endif /* _USBDI_H_ */
diff --git a/sys/legacy/dev/usb/usbdi_util.c b/sys/legacy/dev/usb/usbdi_util.c
new file mode 100644
index 0000000..78ea571
--- /dev/null
+++ b/sys/legacy/dev/usb/usbdi_util.c
@@ -0,0 +1,539 @@
+/* $NetBSD: usbdi_util.c,v 1.42 2004/12/03 08:53:40 augustss 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (usbdebug) printf x
+#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
+extern int usbdebug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+usbd_status
+usbd_get_desc(usbd_device_handle dev, int type, int index, int len, void *desc)
+{
+ usb_device_request_t req;
+
+ DPRINTFN(3,("usbd_get_desc: type=%d, index=%d, len=%d\n",
+ type, index, len));
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, type, index);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+ return (usbd_do_request(dev, &req, desc));
+}
+
+usbd_status
+usbd_get_config_desc(usbd_device_handle dev, int confidx,
+ usb_config_descriptor_t *d)
+{
+ usbd_status err;
+
+ DPRINTFN(3,("usbd_get_config_desc: confidx=%d\n", confidx));
+ err = usbd_get_desc(dev, UDESC_CONFIG, confidx,
+ USB_CONFIG_DESCRIPTOR_SIZE, d);
+ if (err)
+ return (err);
+ if (d->bDescriptorType != UDESC_CONFIG) {
+ DPRINTFN(-1,("usbd_get_config_desc: confidx=%d, bad desc "
+ "len=%d type=%d\n",
+ confidx, d->bLength, d->bDescriptorType));
+ return (USBD_INVAL);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_config_desc_full(usbd_device_handle dev, int conf, void *d, int size)
+{
+ DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf));
+ return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d));
+}
+
+usbd_status
+usbd_get_device_desc(usbd_device_handle dev, usb_device_descriptor_t *d)
+{
+ DPRINTFN(3,("usbd_get_device_desc:\n"));
+ return (usbd_get_desc(dev, UDESC_DEVICE,
+ 0, USB_DEVICE_DESCRIPTOR_SIZE, d));
+}
+
+usbd_status
+usbd_get_device_status(usbd_device_handle dev, usb_status_t *st)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(usb_status_t));
+ return (usbd_do_request(dev, &req, st));
+}
+
+usbd_status
+usbd_get_hub_status(usbd_device_handle dev, usb_hub_status_t *st)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(usb_hub_status_t));
+ return (usbd_do_request(dev, &req, st));
+}
+
+usbd_status
+usbd_set_address(usbd_device_handle dev, int addr)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return usbd_do_request(dev, &req, 0);
+}
+
+usbd_status
+usbd_get_port_status(usbd_device_handle dev, int port, usb_port_status_t *ps)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_CLASS_OTHER;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, port);
+ USETW(req.wLength, sizeof *ps);
+ return (usbd_do_request(dev, &req, ps));
+}
+
+usbd_status
+usbd_clear_hub_feature(usbd_device_handle dev, int sel)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_hub_feature(usbd_device_handle dev, int sel)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_clear_port_feature(usbd_device_handle dev, int port, int sel)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, port);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_port_feature(usbd_device_handle dev, int port, int sel)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, port);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_get_protocol(usbd_interface_handle iface, u_int8_t *report)
+{
+ usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+
+ DPRINTFN(4, ("usbd_get_protocol: iface=%p, endpt=%d\n",
+ iface, id->bInterfaceNumber));
+ if (id == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_PROTOCOL;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, id->bInterfaceNumber);
+ USETW(req.wLength, 1);
+ return (usbd_do_request(dev, &req, report));
+}
+
+usbd_status
+usbd_set_protocol(usbd_interface_handle iface, int report)
+{
+ usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+
+ DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
+ iface, report, id->bInterfaceNumber));
+ if (id == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_PROTOCOL;
+ USETW(req.wValue, report);
+ USETW(req.wIndex, id->bInterfaceNumber);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_set_report(usbd_interface_handle iface, int type, int id, void *data,
+ int len)
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+
+ DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
+ if (ifd == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, type, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, len);
+ return (usbd_do_request(dev, &req, data));
+}
+
+usbd_status
+usbd_set_report_async(usbd_interface_handle iface, int type, int id, void *data,
+ int len)
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+
+ DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len));
+ if (ifd == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, type, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, len);
+ return (usbd_do_request_async(dev, &req, data));
+}
+
+usbd_status
+usbd_get_report(usbd_interface_handle iface, int type, int id, void *data,
+ int len)
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+
+ DPRINTFN(4, ("usbd_get_report: len=%d\n", len));
+ if (ifd == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_REPORT;
+ USETW2(req.wValue, type, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, len);
+ return (usbd_do_request(dev, &req, data));
+}
+
+usbd_status
+usbd_set_idle(usbd_interface_handle iface, int duration, int id)
+{
+ usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
+ usbd_device_handle dev;
+ usb_device_request_t req;
+
+ DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
+ if (ifd == NULL)
+ return (USBD_IOERROR);
+ usbd_interface2device_handle(iface, &dev);
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_IDLE;
+ USETW2(req.wValue, duration, id);
+ USETW(req.wIndex, ifd->bInterfaceNumber);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(dev, &req, 0));
+}
+
+usbd_status
+usbd_get_report_descriptor(usbd_device_handle dev, int ifcno,
+ int size, void *d)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_INTERFACE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */
+ USETW(req.wIndex, ifcno);
+ USETW(req.wLength, size);
+ return (usbd_do_request(dev, &req, d));
+}
+
+usb_hid_descriptor_t *
+usbd_get_hid_descriptor(usbd_interface_handle ifc)
+{
+ usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
+ usbd_device_handle dev;
+ usb_config_descriptor_t *cdesc;
+ usb_hid_descriptor_t *hd;
+ char *p, *end;
+
+ if (idesc == NULL)
+ return (NULL);
+ usbd_interface2device_handle(ifc, &dev);
+ cdesc = usbd_get_config_descriptor(dev);
+
+ p = (char *)idesc + idesc->bLength;
+ end = (char *)cdesc + UGETW(cdesc->wTotalLength);
+
+ for (; p < end; p += hd->bLength) {
+ hd = (usb_hid_descriptor_t *)p;
+ if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
+ return (hd);
+ if (hd->bDescriptorType == UDESC_INTERFACE)
+ break;
+ }
+ return (NULL);
+}
+
+usbd_status
+usbd_read_report_desc(usbd_interface_handle ifc, void **descp, int *sizep,
+ struct malloc_type *mem)
+{
+ usb_interface_descriptor_t *id;
+ usb_hid_descriptor_t *hid;
+ usbd_device_handle dev;
+ usbd_status err;
+
+ usbd_interface2device_handle(ifc, &dev);
+ id = usbd_get_interface_descriptor(ifc);
+ if (id == NULL)
+ return (USBD_INVAL);
+ hid = usbd_get_hid_descriptor(ifc);
+ if (hid == NULL)
+ return (USBD_IOERROR);
+ *sizep = UGETW(hid->descrs[0].wDescriptorLength);
+ *descp = malloc(*sizep, mem, M_NOWAIT);
+ if (*descp == NULL)
+ return (USBD_NOMEM);
+ err = usbd_get_report_descriptor(dev, id->bInterfaceNumber,
+ *sizep, *descp);
+ if (err) {
+ free(*descp, mem);
+ *descp = NULL;
+ return (err);
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_config(usbd_device_handle dev, u_int8_t *conf)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_CONFIG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ return (usbd_do_request(dev, &req, conf));
+}
+
+static void usbd_bulk_transfer_cb(usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status status);
+static void
+usbd_bulk_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ wakeup(xfer);
+}
+
+usbd_status
+usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl)
+{
+ usbd_status err;
+ int s, error;
+
+ usbd_setup_xfer(xfer, pipe, 0, buf, *size,
+ flags, timeout, usbd_bulk_transfer_cb);
+ DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size));
+ s = splusb(); /* don't want callback until tsleep() */
+ err = usbd_transfer(xfer);
+ if (err != USBD_IN_PROGRESS) {
+ splx(s);
+ return (err);
+ }
+ error = tsleep((caddr_t)xfer, PZERO | PCATCH, lbl, 0);
+ splx(s);
+ if (error) {
+ DPRINTF(("usbd_bulk_transfer: tsleep=%d\n", error));
+ usbd_abort_pipe(pipe);
+ return (USBD_INTERRUPTED);
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
+ DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
+ if (err) {
+ DPRINTF(("usbd_bulk_transfer: error=%d\n", err));
+ usbd_clear_endpoint_stall(pipe);
+ }
+ return (err);
+}
+
+static void usbd_intr_transfer_cb(usbd_xfer_handle xfer,
+ usbd_private_handle priv, usbd_status status);
+static void
+usbd_intr_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ wakeup(xfer);
+}
+
+usbd_status
+usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl)
+{
+ usbd_status err;
+ int s, error;
+
+ usbd_setup_xfer(xfer, pipe, 0, buf, *size,
+ flags, timeout, usbd_intr_transfer_cb);
+ DPRINTFN(1, ("usbd_intr_transfer: start transfer %d bytes\n", *size));
+ s = splusb(); /* don't want callback until tsleep() */
+ err = usbd_transfer(xfer);
+ if (err != USBD_IN_PROGRESS) {
+ splx(s);
+ return (err);
+ }
+ error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
+ splx(s);
+ if (error) {
+ DPRINTF(("usbd_intr_transfer: tsleep=%d\n", error));
+ usbd_abort_pipe(pipe);
+ return (USBD_INTERRUPTED);
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
+ DPRINTFN(1,("usbd_intr_transfer: transferred %d\n", *size));
+ if (err) {
+ DPRINTF(("usbd_intr_transfer: error=%d\n", err));
+ usbd_clear_endpoint_stall(pipe);
+ }
+ return (err);
+}
+
+void
+usb_detach_wait(device_t dv)
+{
+ DPRINTF(("usb_detach_wait: waiting for %s\n", device_get_nameunit(dv)));
+ if (tsleep(dv, PZERO, "usbdet", hz * 60))
+ printf("usb_detach_wait: %s didn't detach\n",
+ device_get_nameunit(dv));
+ DPRINTF(("usb_detach_wait: %s done\n", device_get_nameunit(dv)));
+}
+
+void
+usb_detach_wakeup(device_t dv)
+{
+ DPRINTF(("usb_detach_wakeup: for %s\n", device_get_nameunit(dv)));
+ wakeup(dv);
+}
+
+const usb_descriptor_t *
+usb_find_desc(usbd_device_handle dev, int type, int subtype)
+{
+ usbd_desc_iter_t iter;
+ const usb_descriptor_t *desc;
+
+ usb_desc_iter_init(dev, &iter);
+ for (;;) {
+ desc = usb_desc_iter_next(&iter);
+ if (!desc || (desc->bDescriptorType == type &&
+ (subtype == USBD_SUBTYPE_ANY ||
+ subtype == desc->bDescriptorSubtype)))
+ break;
+ }
+ return desc;
+}
diff --git a/sys/legacy/dev/usb/usbdi_util.h b/sys/legacy/dev/usb/usbdi_util.h
new file mode 100644
index 0000000..b535f0c
--- /dev/null
+++ b/sys/legacy/dev/usb/usbdi_util.h
@@ -0,0 +1,98 @@
+/* $NetBSD: usbdi_util.h,v 1.31 2004/12/03 08:53:40 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+#ifndef _USBI_UTIL_H_
+#define _USBI_UTIL_H_
+usbd_status usbd_get_desc(usbd_device_handle dev, int type,
+ int index, int len, void *desc);
+usbd_status usbd_get_config_desc(usbd_device_handle, int,
+ usb_config_descriptor_t *);
+usbd_status usbd_get_config_desc_full(usbd_device_handle, int, void *, int);
+usbd_status usbd_get_device_desc(usbd_device_handle dev,
+ usb_device_descriptor_t *d);
+usbd_status usbd_set_address(usbd_device_handle dev, int addr);
+usbd_status usbd_get_port_status(usbd_device_handle,
+ int, usb_port_status_t *);
+usbd_status usbd_set_hub_feature(usbd_device_handle dev, int);
+usbd_status usbd_clear_hub_feature(usbd_device_handle, int);
+usbd_status usbd_set_port_feature(usbd_device_handle dev, int, int);
+usbd_status usbd_clear_port_feature(usbd_device_handle, int, int);
+usbd_status usbd_get_device_status(usbd_device_handle, usb_status_t *);
+usbd_status usbd_get_hub_status(usbd_device_handle, usb_hub_status_t *);
+usbd_status usbd_get_protocol(usbd_interface_handle dev, u_int8_t *report);
+usbd_status usbd_set_protocol(usbd_interface_handle dev, int report);
+usbd_status usbd_get_report_descriptor(usbd_device_handle dev, int ifcno,
+ int size, void *d);
+struct usb_hid_descriptor *usbd_get_hid_descriptor(usbd_interface_handle ifc);
+usbd_status usbd_set_report(usbd_interface_handle iface, int type, int id,
+ void *data,int len);
+usbd_status usbd_set_report_async(usbd_interface_handle iface, int type,
+ int id, void *data, int len);
+usbd_status usbd_get_report(usbd_interface_handle iface, int type, int id,
+ void *data, int len);
+usbd_status usbd_set_idle(usbd_interface_handle iface, int duration, int id);
+usbd_status usbd_read_report_desc(usbd_interface_handle ifc, void **descp,
+ int *sizep, struct malloc_type *mem);
+usbd_status usbd_get_config(usbd_device_handle dev, u_int8_t *conf);
+usbd_status usbd_get_string_desc(usbd_device_handle dev, int sindex,
+ int langid,usb_string_descriptor_t *sdesc,
+ int *sizep);
+void usbd_delay_ms(usbd_device_handle, u_int);
+
+
+usbd_status usbd_set_config_no(usbd_device_handle dev, int no, int msg);
+usbd_status usbd_set_config_index(usbd_device_handle dev, int index, int msg);
+
+usbd_status usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl);
+
+usbd_status usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
+ u_int16_t flags, u_int32_t timeout, void *buf,
+ u_int32_t *size, char *lbl);
+
+void usb_detach_wait(device_t);
+void usb_detach_wakeup(device_t);
+
+const usb_descriptor_t *usb_find_desc(usbd_device_handle dev, int type,
+ int subtype);
+#define USBD_SUBTYPE_ANY (~0)
+
+#endif /* _USBI_UTIL_H_ */
diff --git a/sys/legacy/dev/usb/usbdivar.h b/sys/legacy/dev/usb/usbdivar.h
new file mode 100644
index 0000000..603d691
--- /dev/null
+++ b/sys/legacy/dev/usb/usbdivar.h
@@ -0,0 +1,322 @@
+/* $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+/* From usb_mem.h */
+struct usb_dma_block;
+typedef struct {
+ struct usb_dma_block *block;
+ u_int offs;
+ u_int len;
+} usb_dma_t;
+
+struct usbd_xfer;
+struct usbd_pipe;
+
+struct usbd_endpoint {
+ usb_endpoint_descriptor_t *edesc;
+ int refcnt;
+ int savedtoggle;
+};
+
+struct usbd_bus_methods {
+ usbd_status (*open_pipe)(struct usbd_pipe *pipe);
+ void (*soft_intr)(void *);
+ void (*do_poll)(struct usbd_bus *);
+ usbd_status (*allocm)(struct usbd_bus *, usb_dma_t *,
+ u_int32_t bufsize);
+ void (*freem)(struct usbd_bus *, usb_dma_t *);
+ struct usbd_xfer * (*allocx)(struct usbd_bus *);
+ void (*freex)(struct usbd_bus *, struct usbd_xfer *);
+};
+
+struct usbd_pipe_methods {
+ usbd_status (*transfer)(usbd_xfer_handle xfer);
+ usbd_status (*start)(usbd_xfer_handle xfer);
+ void (*abort)(usbd_xfer_handle xfer);
+ void (*close)(usbd_pipe_handle pipe);
+ void (*cleartoggle)(usbd_pipe_handle pipe);
+ void (*done)(usbd_xfer_handle xfer);
+};
+
+struct usbd_tt {
+ struct usbd_hub *hub;
+};
+
+struct usbd_port {
+ usb_port_status_t status;
+ u_int16_t power; /* mA of current on port */
+ u_int8_t portno;
+ u_int8_t restartcnt;
+#define USBD_RESTART_MAX 5
+ struct usbd_device *device; /* Connected device */
+ struct usbd_device *parent; /* The ports hub */
+ struct usbd_tt *tt; /* Transaction translator (if any) */
+};
+
+struct usbd_hub {
+ usbd_status (*explore)(usbd_device_handle hub);
+ void *hubsoftc;
+ usb_hub_descriptor_t hubdesc;
+ struct usbd_port ports[1];
+};
+
+struct usb_softc;
+
+/*****/
+
+struct usbd_bus {
+ /* Filled by HC driver */
+ device_t bdev; /* base device, host adapter */
+ struct usbd_bus_methods *methods;
+ u_int32_t pipe_size; /* size of a pipe struct */
+ /* Filled by usb driver */
+ struct usbd_device *root_hub;
+ usbd_device_handle devices[USB_MAX_DEVICES];
+ char needs_explore;/* a hub a signalled a change */
+ char use_polling;
+ struct usb_softc *usbctl;
+ struct usb_device_stats stats;
+ int intr_context;
+ u_int no_intrs;
+ int usbrev; /* USB revision */
+#define USBREV_UNKNOWN 0
+#define USBREV_PRE_1_0 1
+#define USBREV_1_0 2
+#define USBREV_1_1 3
+#define USBREV_2_0 4
+#define USBREV_STR { "unknown", "pre 1.0", "1.0", "1.1", "2.0" }
+
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ void *soft; /* soft interrupt cookie */
+#else
+ struct callout softi;
+#endif
+#endif
+
+ bus_dma_tag_t parent_dmatag; /* Base DMA tag */
+ bus_dma_tag_t buffer_dmatag; /* Tag for transfer buffers */
+};
+
+struct usbd_device {
+ struct usbd_bus *bus; /* our controller */
+ struct usbd_pipe *default_pipe; /* pipe 0 */
+ u_int8_t address; /* device addess */
+ u_int8_t config; /* current configuration # */
+ u_int8_t depth; /* distance from root hub */
+ u_int8_t speed; /* low/full/high speed */
+ u_int8_t self_powered; /* flag for self powered */
+ u_int16_t power; /* mA the device uses */
+ int16_t langid; /* language for strings */
+#define USBD_NOLANG (-1)
+ usb_event_cookie_t cookie; /* unique connection id */
+ struct usbd_port *powersrc; /* upstream hub port, or 0 */
+ struct usbd_device *myhub; /* upstream hub */
+ struct usbd_port *myhsport; /* closest high speed port */
+ struct usbd_endpoint def_ep; /* for pipe 0 */
+ usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */
+ struct usbd_interface *ifaces; /* array of all interfaces */
+ usb_device_descriptor_t ddesc; /* device descriptor */
+ usb_config_descriptor_t *cdesc; /* full config descr */
+ const struct usbd_quirks *quirks; /* device quirks, always set */
+ struct usbd_hub *hub; /* only if this is a hub */
+ device_t *subdevs; /* sub-devices, 0 terminated */
+ uint8_t *ifacenums; /* sub-device interfacenumbers */
+};
+
+struct usbd_interface {
+ struct usbd_device *device;
+ usb_interface_descriptor_t *idesc;
+ int index;
+ int altindex;
+ struct usbd_endpoint *endpoints;
+ void *priv;
+ LIST_HEAD(, usbd_pipe) pipes;
+};
+
+struct usbd_pipe {
+ struct usbd_interface *iface;
+ struct usbd_device *device;
+ struct usbd_endpoint *endpoint;
+ int refcnt;
+ char running;
+ char aborting;
+ STAILQ_HEAD(, usbd_xfer) queue;
+ LIST_ENTRY(usbd_pipe) next;
+
+ usbd_xfer_handle intrxfer; /* used for repeating requests */
+ char repeat;
+ int interval;
+
+ /* Filled by HC driver. */
+ struct usbd_pipe_methods *methods;
+};
+
+#define USB_DMA_NSEG (btoc(MAXPHYS) + 1)
+
+/* DMA-capable memory buffer. */
+struct usb_dma_mapping {
+ bus_dma_segment_t segs[USB_DMA_NSEG]; /* The physical segments. */
+ int nsegs; /* Number of segments. */
+ bus_dmamap_t map; /* DMA mapping. */
+};
+
+struct usbd_xfer {
+ struct usbd_pipe *pipe;
+ void *priv;
+ void *buffer;
+ u_int32_t length;
+ u_int32_t actlen;
+ u_int16_t flags;
+ u_int32_t timeout;
+ usbd_status status;
+ usbd_callback callback;
+ __volatile char done;
+#ifdef DIAGNOSTIC
+ u_int32_t busy_free;
+#define XFER_FREE 0x46524545
+#define XFER_BUSY 0x42555359
+#define XFER_ONQU 0x4f4e5155
+#endif
+
+ /* For control pipe */
+ usb_device_request_t request;
+
+ /* For isoc */
+ u_int16_t *frlengths;
+ int nframes;
+
+ /* For memory allocation */
+ struct usbd_device *device;
+ struct usb_dma_mapping dmamap;
+ void *allocbuf;
+
+ int rqflags;
+#define URQ_REQUEST 0x01
+#define URQ_AUTO_DMABUF 0x10
+#define URQ_DEV_DMABUF 0x20
+
+ STAILQ_ENTRY(usbd_xfer) next;
+
+ void *hcpriv; /* private use by the HC driver */
+
+ struct callout timeout_handle;
+};
+
+#define URQ_BITS "\20\1REQUEST\5AUTO_DMABUF\6DEV_DMABUF"
+
+void usbd_init(void);
+void usbd_finish(void);
+
+#ifdef USB_DEBUG
+void usbd_dump_iface(struct usbd_interface *iface);
+void usbd_dump_device(struct usbd_device *dev);
+void usbd_dump_endpoint(struct usbd_endpoint *endp);
+void usbd_dump_queue(usbd_pipe_handle pipe);
+void usbd_dump_pipe(usbd_pipe_handle pipe);
+#endif
+
+/* Routines from usb_subr.c */
+int usbctlprint(void *, const char *);
+void usb_delay_ms(usbd_bus_handle, u_int);
+usbd_status usbd_reset_port(usbd_device_handle dev,
+ int port, usb_port_status_t *ps);
+usbd_status usbd_setup_pipe(usbd_device_handle dev,
+ usbd_interface_handle iface,
+ struct usbd_endpoint *, int,
+ usbd_pipe_handle *pipe);
+usbd_status usbd_new_device(device_t parent,
+ usbd_bus_handle bus, int depth,
+ int lowspeed, int port,
+ struct usbd_port *);
+void usbd_remove_device(usbd_device_handle, struct usbd_port *);
+int usbd_printBCD(char *cp, int bcd);
+usbd_status usbd_fill_iface_data(usbd_device_handle dev, int i, int a);
+void usb_free_device(usbd_device_handle);
+
+usbd_status usb_insert_transfer(usbd_xfer_handle xfer);
+void usb_transfer_complete(usbd_xfer_handle xfer);
+void usb_disconnect_port(struct usbd_port *up, device_t);
+
+/* Routines from usb.c */
+void usb_needs_explore(usbd_device_handle);
+void usb_schedsoftintr(struct usbd_bus *);
+
+/*
+ * XXX This check is extremely bogus. Bad Bad Bad.
+ */
+#if defined(DIAGNOSTIC) && 0
+#define SPLUSBCHECK \
+ do { int _s = splusb(), _su = splusb(); \
+ if (!cold && _s != _su) printf("SPLUSBCHECK failed 0x%x!=0x%x, %s:%d\n", \
+ _s, _su, __FILE__, __LINE__); \
+ splx(_s); \
+ } while (0)
+#else
+#define SPLUSBCHECK
+#endif
+
+/* Locator stuff. */
+
+/* XXX these values are used to statically bind some elements in the USB tree
+ * to specific driver instances. This should be somehow emulated in FreeBSD
+ * but can be done later on.
+ * The values are copied from the files.usb file in the NetBSD sources.
+ */
+#define UHUBCF_PORT_DEFAULT -1
+#define UHUBCF_CONFIGURATION_DEFAULT -1
+#define UHUBCF_INTERFACE_DEFAULT -1
+#define UHUBCF_VENDOR_DEFAULT -1
+#define UHUBCF_PRODUCT_DEFAULT -1
+#define UHUBCF_RELEASE_DEFAULT -1
+
+#define uhubcf_port cf_loc[UHUBCF_PORT]
+#define uhubcf_configuration cf_loc[UHUBCF_CONFIGURATION]
+#define uhubcf_interface cf_loc[UHUBCF_INTERFACE]
+#define uhubcf_vendor cf_loc[UHUBCF_VENDOR]
+#define uhubcf_product cf_loc[UHUBCF_PRODUCT]
+#define uhubcf_release cf_loc[UHUBCF_RELEASE]
+#define UHUB_UNK_PORT UHUBCF_PORT_DEFAULT /* wildcarded 'port' */
+#define UHUB_UNK_CONFIGURATION UHUBCF_CONFIGURATION_DEFAULT /* wildcarded 'configuration' */
+#define UHUB_UNK_INTERFACE UHUBCF_INTERFACE_DEFAULT /* wildcarded 'interface' */
+#define UHUB_UNK_VENDOR UHUBCF_VENDOR_DEFAULT /* wildcarded 'vendor' */
+#define UHUB_UNK_PRODUCT UHUBCF_PRODUCT_DEFAULT /* wildcarded 'product' */
+#define UHUB_UNK_RELEASE UHUBCF_RELEASE_DEFAULT /* wildcarded 'release' */
+
diff --git a/sys/legacy/dev/usb/usbhid.h b/sys/legacy/dev/usb/usbhid.h
new file mode 100644
index 0000000..8e0ecd5
--- /dev/null
+++ b/sys/legacy/dev/usb/usbhid.h
@@ -0,0 +1,185 @@
+/* $NetBSD: usbhid.h,v 1.9 2000/09/03 19:09:14 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * 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.
+ */
+
+
+#ifndef _USBHID_H_
+#define _USBHID_H_
+
+#define UR_GET_HID_DESCRIPTOR 0x06
+#define UDESC_HID 0x21
+#define UDESC_REPORT 0x22
+#define UDESC_PHYSICAL 0x23
+#define UR_SET_HID_DESCRIPTOR 0x07
+#define UR_GET_REPORT 0x01
+#define UR_SET_REPORT 0x09
+#define UR_GET_IDLE 0x02
+#define UR_SET_IDLE 0x0a
+#define UR_GET_PROTOCOL 0x03
+#define UR_SET_PROTOCOL 0x0b
+
+typedef struct usb_hid_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdHID;
+ uByte bCountryCode;
+ uByte bNumDescriptors;
+ struct {
+ uByte bDescriptorType;
+ uWord wDescriptorLength;
+ } descrs[1];
+} UPACKED usb_hid_descriptor_t;
+#define USB_HID_DESCRIPTOR_SIZE(n) (9+(n)*3)
+
+/* Usage pages */
+#define HUP_UNDEFINED 0x0000
+#define HUP_GENERIC_DESKTOP 0x0001
+#define HUP_SIMULATION 0x0002
+#define HUP_VR_CONTROLS 0x0003
+#define HUP_SPORTS_CONTROLS 0x0004
+#define HUP_GAMING_CONTROLS 0x0005
+#define HUP_KEYBOARD 0x0007
+#define HUP_LEDS 0x0008
+#define HUP_BUTTON 0x0009
+#define HUP_ORDINALS 0x000a
+#define HUP_TELEPHONY 0x000b
+#define HUP_CONSUMER 0x000c
+#define HUP_DIGITIZERS 0x000d
+#define HUP_PHYSICAL_IFACE 0x000e
+#define HUP_UNICODE 0x0010
+#define HUP_ALPHANUM_DISPLAY 0x0014
+#define HUP_MONITOR 0x0080
+#define HUP_MONITOR_ENUM_VAL 0x0081
+#define HUP_VESA_VC 0x0082
+#define HUP_VESA_CMD 0x0083
+#define HUP_POWER 0x0084
+#define HUP_BATTERY_SYSTEM 0x0085
+#define HUP_BARCODE_SCANNER 0x008b
+#define HUP_SCALE 0x008c
+#define HUP_CAMERA_CONTROL 0x0090
+#define HUP_ARCADE 0x0091
+#define HUP_MICROSOFT 0xff00
+
+/* Usages, generic desktop */
+#define HUG_POINTER 0x0001
+#define HUG_MOUSE 0x0002
+#define HUG_JOYSTICK 0x0004
+#define HUG_GAME_PAD 0x0005
+#define HUG_KEYBOARD 0x0006
+#define HUG_KEYPAD 0x0007
+#define HUG_X 0x0030
+#define HUG_Y 0x0031
+#define HUG_Z 0x0032
+#define HUG_RX 0x0033
+#define HUG_RY 0x0034
+#define HUG_RZ 0x0035
+#define HUG_SLIDER 0x0036
+#define HUG_DIAL 0x0037
+#define HUG_WHEEL 0x0038
+#define HUG_HAT_SWITCH 0x0039
+#define HUG_COUNTED_BUFFER 0x003a
+#define HUG_BYTE_COUNT 0x003b
+#define HUG_MOTION_WAKEUP 0x003c
+#define HUG_VX 0x0040
+#define HUG_VY 0x0041
+#define HUG_VZ 0x0042
+#define HUG_VBRX 0x0043
+#define HUG_VBRY 0x0044
+#define HUG_VBRZ 0x0045
+#define HUG_VNO 0x0046
+#define HUG_TWHEEL 0x0048 // M$ Wireless Intellimouse Wheel
+#define HUG_SYSTEM_CONTROL 0x0080
+#define HUG_SYSTEM_POWER_DOWN 0x0081
+#define HUG_SYSTEM_SLEEP 0x0082
+#define HUG_SYSTEM_WAKEUP 0x0083
+#define HUG_SYSTEM_CONTEXT_MENU 0x0084
+#define HUG_SYSTEM_MAIN_MENU 0x0085
+#define HUG_SYSTEM_APP_MENU 0x0086
+#define HUG_SYSTEM_MENU_HELP 0x0087
+#define HUG_SYSTEM_MENU_EXIT 0x0088
+#define HUG_SYSTEM_MENU_SELECT 0x0089
+#define HUG_SYSTEM_MENU_RIGHT 0x008a
+#define HUG_SYSTEM_MENU_LEFT 0x008b
+#define HUG_SYSTEM_MENU_UP 0x008c
+#define HUG_SYSTEM_MENU_DOWN 0x008d
+
+/* Usages Digitizers */
+#define HUD_UNDEFINED 0x0000
+#define HUD_TIP_PRESSURE 0x0030
+#define HUD_BARREL_PRESSURE 0x0031
+#define HUD_IN_RANGE 0x0032
+#define HUD_TOUCH 0x0033
+#define HUD_UNTOUCH 0x0034
+#define HUD_TAP 0x0035
+#define HUD_QUALITY 0x0036
+#define HUD_DATA_VALID 0x0037
+#define HUD_TRANSDUCER_INDEX 0x0038
+#define HUD_TABLET_FKEYS 0x0039
+#define HUD_PROGRAM_CHANGE_KEYS 0x003a
+#define HUD_BATTERY_STRENGTH 0x003b
+#define HUD_INVERT 0x003c
+#define HUD_X_TILT 0x003d
+#define HUD_Y_TILT 0x003e
+#define HUD_AZIMUTH 0x003f
+#define HUD_ALTITUDE 0x0040
+#define HUD_TWIST 0x0041
+#define HUD_TIP_SWITCH 0x0042
+#define HUD_SEC_TIP_SWITCH 0x0043
+#define HUD_BARREL_SWITCH 0x0044
+#define HUD_ERASER 0x0045
+#define HUD_TABLET_PICK 0x0046
+
+#define HID_USAGE2(p,u) (((p) << 16) | u)
+
+#define UHID_INPUT_REPORT 0x01
+#define UHID_OUTPUT_REPORT 0x02
+#define UHID_FEATURE_REPORT 0x03
+
+/* Bits in the input/output/feature items */
+#define HIO_CONST 0x001
+#define HIO_VARIABLE 0x002
+#define HIO_RELATIVE 0x004
+#define HIO_WRAP 0x008
+#define HIO_NONLINEAR 0x010
+#define HIO_NOPREF 0x020
+#define HIO_NULLSTATE 0x040
+#define HIO_VOLATILE 0x080
+#define HIO_BUFBYTES 0x100
+
+#endif /* _USBHID_H_ */
diff --git a/sys/legacy/dev/usb/uscanner.c b/sys/legacy/dev/usb/uscanner.c
new file mode 100644
index 0000000..bff3a48
--- /dev/null
+++ b/sys/legacy/dev/usb/uscanner.c
@@ -0,0 +1,723 @@
+/* $NetBSD: uscanner.c,v 1.30 2002/07/11 21:14:36 augustss Exp$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uscanner.c,v 1.33 2002/09/23 05:51:24 simonb Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2000 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
+ * and Nick Hibma (n_hibma@qubesoft.com).
+ *
+ * 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.
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/conf.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include "usbdevs.h"
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uscannerdebug) printf x
+#define DPRINTFN(n,x) if (uscannerdebug>(n)) printf x
+int uscannerdebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uscanner, CTLFLAG_RW, 0, "USB uscanner");
+SYSCTL_INT(_hw_usb_uscanner, OID_AUTO, debug, CTLFLAG_RW,
+ &uscannerdebug, 0, "uscanner debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct uscan_info {
+ struct usb_devno devno;
+ u_int flags;
+#define USC_KEEP_OPEN 1
+};
+
+/* Table of scanners that may work with this driver. */
+static const struct uscan_info uscanner_devs[] = {
+
+ /*
+ * These first two entries are duplicates of known-working units,
+ * so one can patch them to test support for newer devices
+ * without rebuilding the module.
+ */
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000 }, 0 }, /* duplicate */
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000 }, 0 }, /* duplicate */
+
+ /* Acer Peripherals */
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_4300U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640BT }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_1240U }, 0 },
+ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U }, 0 },
+
+ /* AGFA */
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26 }, 0 },
+ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52 }, 0 },
+
+ /* Avision */
+ {{ USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U }, 0 },
+
+ /* Canon */
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_D660U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U }, 0 },
+ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_LIDE25 }, 0 },
+
+ /* Kye */
+ {{ USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO }, 0 },
+
+ /* HP */
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_2200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_3300C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4100C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4300C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4470C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_4670V }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_S20 }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_5200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_5300C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_5400C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_6200C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_6300C }, 0 },
+ {{ USB_VENDOR_HP, USB_PRODUCT_HP_82x0C }, 0 },
+
+ /* Microtek */
+ {{ USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2 }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6 }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2 }, 0 },
+ {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL }, 0 },
+
+ /* Minolta */
+ {{ USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400 }, 0 },
+
+ /* Mustek */
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS }, 0 },
+ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS }, 0 },
+
+ /* National */
+ {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200 }, 0 },
+ {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400 }, 0 },
+
+ /* Nikon */
+ {{ USB_VENDOR_NIKON, USB_PRODUCT_NIKON_LS40 }, 0 },
+
+ /* Primax */
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600 }, 0 },
+ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600 }, 0 },
+
+ /* Epson */
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1270 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_RX425 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200 }, USC_KEEP_OPEN },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F }, USC_KEEP_OPEN },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_2480 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3500 }, USC_KEEP_OPEN },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3590 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4200 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4800 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4990 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_5000 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_CX5400 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX7400 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX8400 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_CX5400 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX3800 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX4000 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_NX300 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_SX200 }, 0 },
+ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_SX400 }, 0 },
+
+ /* UMAX */
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U }, 0 },
+ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400 }, 0 },
+
+ /* Visioneer */
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100 }, 0 },
+ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600 }, 0 },
+
+ /* Ultima */
+ {{ USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS }, 0 },
+
+};
+#define uscanner_lookup(v, p) ((const struct uscan_info *)usb_lookup(uscanner_devs, v, p))
+
+#define USCANNER_BUFFERSIZE 1024
+
+struct uscanner_softc {
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+ struct cdev *dev;
+
+ u_int sc_dev_flags;
+
+ usbd_pipe_handle sc_bulkin_pipe;
+ int sc_bulkin;
+ usbd_xfer_handle sc_bulkin_xfer;
+ void *sc_bulkin_buffer;
+ int sc_bulkin_bufferlen;
+ int sc_bulkin_datalen;
+
+ usbd_pipe_handle sc_bulkout_pipe;
+ int sc_bulkout;
+ usbd_xfer_handle sc_bulkout_xfer;
+ void *sc_bulkout_buffer;
+ int sc_bulkout_bufferlen;
+ int sc_bulkout_datalen;
+
+ u_char sc_state;
+#define USCANNER_OPEN 0x01 /* opened */
+
+ int sc_refcnt;
+ u_char sc_dying;
+};
+
+d_open_t uscanneropen;
+d_close_t uscannerclose;
+d_read_t uscannerread;
+d_write_t uscannerwrite;
+d_poll_t uscannerpoll;
+
+
+static struct cdevsw uscanner_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = uscanneropen,
+ .d_close = uscannerclose,
+ .d_read = uscannerread,
+ .d_write = uscannerwrite,
+ .d_poll = uscannerpoll,
+ .d_name = "uscanner",
+};
+
+static int uscanner_do_read(struct uscanner_softc *, struct uio *, int);
+static int uscanner_do_write(struct uscanner_softc *, struct uio *, int);
+static void uscanner_do_close(struct uscanner_softc *);
+
+#define USCANNERUNIT(n) (dev2unit(n))
+
+static device_probe_t uscanner_match;
+static device_attach_t uscanner_attach;
+static device_detach_t uscanner_detach;
+
+static device_method_t uscanner_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uscanner_match),
+ DEVMETHOD(device_attach, uscanner_attach),
+ DEVMETHOD(device_detach, uscanner_detach),
+
+ { 0, 0 }
+};
+
+static driver_t uscanner_driver = {
+ "uscanner",
+ uscanner_methods,
+ sizeof(struct uscanner_softc)
+};
+
+static devclass_t uscanner_devclass;
+
+static int
+uscanner_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id;
+
+ if (uaa->iface == NULL)
+ return UMATCH_NONE; /* do not grab the entire device */
+
+ if (uscanner_lookup(uaa->vendor, uaa->product) == NULL)
+ return UMATCH_NONE; /* not in the list of known devices */
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (id == NULL)
+ return UMATCH_NONE;
+
+ /*
+ * There isn't a specific UICLASS for scanners, many vendors use
+ * UICLASS_VENDOR, so detecting the right interface is not so easy.
+ * But certainly we can exclude PRINTER and MASS - which some
+ * multifunction devices implement.
+ */
+ if (id->bInterfaceClass == UICLASS_PRINTER ||
+ id->bInterfaceClass == UICLASS_MASS)
+ return UMATCH_NONE;
+
+ return UMATCH_VENDOR_PRODUCT; /* ok we found it */
+}
+
+static int
+uscanner_attach(device_t self)
+{
+ struct uscanner_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usb_interface_descriptor_t *id = 0;
+ usb_endpoint_descriptor_t *ed, *ed_bulkin = NULL, *ed_bulkout = NULL;
+ int i;
+ usbd_status err;
+ int ifnum;
+
+ sc->sc_dev = self;
+ sc->sc_dev_flags = uscanner_lookup(uaa->vendor, uaa->product)->flags;
+ sc->sc_udev = uaa->device;
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ ifnum = id->bInterfaceNumber;
+#if 0
+ /*
+ * This was in the original driver, but we cannot change the
+ * configuration of the whole device while attaching only to
+ * one of its interfaces. This can kill other already-attached
+ * driver, and/or possibly prevent this driver from attaching
+ * if an error occurs in set_config_no.
+ * If a device need setting the configuration, this must be done
+ * before attaching drivers to the various interfaces.
+ */
+ err = usbd_set_config_no(uaa->device, 1, 1); /* XXX */
+ if (err) {
+ printf("%s: setting config no failed\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+#endif
+ err = usbd_device2interface_handle(sc->sc_udev, ifnum, &sc->sc_iface);
+ if (!err && sc->sc_iface)
+ id = usbd_get_interface_descriptor(sc->sc_iface);
+ if (err || id == 0) {
+ printf("%s: could not get interface descriptor, err=%d,id=%p\n",
+ device_get_nameunit(sc->sc_dev), err, id);
+ return ENXIO;
+ }
+
+ /* Find the two first bulk endpoints */
+ for (i = 0 ; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == 0) {
+ printf("%s: could not read endpoint descriptor\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkin = ed;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT
+ && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ ed_bulkout = ed;
+ }
+
+ if (ed_bulkin && ed_bulkout) /* found all we need */
+ break;
+ }
+
+ /* Verify that we goething sensible */
+ if (ed_bulkin == NULL || ed_bulkout == NULL) {
+ printf("%s: bulk-in and/or bulk-out endpoint not found\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+
+ sc->sc_bulkin = ed_bulkin->bEndpointAddress;
+ sc->sc_bulkout = ed_bulkout->bEndpointAddress;
+
+ /* the main device, ctrl endpoint */
+ sc->dev = make_dev(&uscanner_cdevsw, device_get_unit(sc->sc_dev),
+ UID_ROOT, GID_OPERATOR, 0644, "%s", device_get_nameunit(sc->sc_dev));
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,sc->sc_dev);
+
+ return 0;
+}
+
+int
+uscanneropen(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct uscanner_softc *sc;
+ int unit = USCANNERUNIT(dev);
+ usbd_status err;
+
+ sc = devclass_get_softc(uscanner_devclass, unit);
+ if (sc == NULL)
+ return (ENXIO);
+
+ DPRINTFN(5, ("uscanneropen: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, unit));
+
+ if (sc->sc_dying)
+ return (ENXIO);
+
+ if (sc->sc_state & USCANNER_OPEN)
+ return (EBUSY);
+
+ sc->sc_state |= USCANNER_OPEN;
+
+ sc->sc_bulkin_buffer = malloc(USCANNER_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ sc->sc_bulkout_buffer = malloc(USCANNER_BUFFERSIZE, M_USBDEV, M_WAITOK);
+ /* No need to check buffers for NULL since we have WAITOK */
+
+ sc->sc_bulkin_bufferlen = USCANNER_BUFFERSIZE;
+ sc->sc_bulkout_bufferlen = USCANNER_BUFFERSIZE;
+
+ /* We have decided on which endpoints to use, now open the pipes */
+ if (sc->sc_bulkin_pipe == NULL) {
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
+ if (err) {
+ printf("%s: cannot open bulk-in pipe (addr %d)\n",
+ device_get_nameunit(sc->sc_dev), sc->sc_bulkin);
+ uscanner_do_close(sc);
+ return (EIO);
+ }
+ }
+ if (sc->sc_bulkout_pipe == NULL) {
+ err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
+ if (err) {
+ printf("%s: cannot open bulk-out pipe (addr %d)\n",
+ device_get_nameunit(sc->sc_dev), sc->sc_bulkout);
+ uscanner_do_close(sc);
+ return (EIO);
+ }
+ }
+
+ sc->sc_bulkin_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulkin_xfer == NULL) {
+ uscanner_do_close(sc);
+ return (ENOMEM);
+ }
+ sc->sc_bulkout_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulkout_xfer == NULL) {
+ uscanner_do_close(sc);
+ return (ENOMEM);
+ }
+
+ return (0); /* success */
+}
+
+int
+uscannerclose(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ struct uscanner_softc *sc;
+
+ sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev));
+ DPRINTFN(5, ("uscannerclose: flag=%d, mode=%d, unit=%d\n",
+ flag, mode, USCANNERUNIT(dev)));
+
+#ifdef DIAGNOSTIC
+ if (!(sc->sc_state & USCANNER_OPEN)) {
+ printf("uscannerclose: not open\n");
+ return (EINVAL);
+ }
+#endif
+
+ uscanner_do_close(sc);
+
+ return (0);
+}
+
+void
+uscanner_do_close(struct uscanner_softc *sc)
+{
+ if (sc->sc_bulkin_xfer) {
+ usbd_free_xfer(sc->sc_bulkin_xfer);
+ sc->sc_bulkin_xfer = NULL;
+ }
+ if (sc->sc_bulkout_xfer) {
+ usbd_free_xfer(sc->sc_bulkout_xfer);
+ sc->sc_bulkout_xfer = NULL;
+ }
+
+ if (!(sc->sc_dev_flags & USC_KEEP_OPEN)) {
+ if (sc->sc_bulkin_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ usbd_close_pipe(sc->sc_bulkin_pipe);
+ sc->sc_bulkin_pipe = NULL;
+ }
+ if (sc->sc_bulkout_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+ usbd_close_pipe(sc->sc_bulkout_pipe);
+ sc->sc_bulkout_pipe = NULL;
+ }
+ }
+
+ if (sc->sc_bulkin_buffer) {
+ free(sc->sc_bulkin_buffer, M_USBDEV);
+ sc->sc_bulkin_buffer = NULL;
+ }
+ if (sc->sc_bulkout_buffer) {
+ free(sc->sc_bulkout_buffer, M_USBDEV);
+ sc->sc_bulkout_buffer = NULL;
+ }
+
+ sc->sc_state &= ~USCANNER_OPEN;
+}
+
+static int
+uscanner_do_read(struct uscanner_softc *sc, struct uio *uio, int flag)
+{
+ u_int32_t n, tn;
+ usbd_status err;
+ int error = 0;
+
+ DPRINTFN(5, ("%s: uscannerread\n", device_get_nameunit(sc->sc_dev)));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ while ((n = min(sc->sc_bulkin_bufferlen, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("uscannerread: start transfer %d bytes\n",n));
+ tn = n;
+
+ err = usbd_bulk_transfer(
+ sc->sc_bulkin_xfer, sc->sc_bulkin_pipe,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+ sc->sc_bulkin_buffer, &tn,
+ "uscnrb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else if (err == USBD_TIMEOUT)
+ error = ETIMEDOUT;
+ else
+ error = EIO;
+ break;
+ }
+ DPRINTFN(1, ("uscannerread: got %d bytes\n", tn));
+ error = uiomove(sc->sc_bulkin_buffer, tn, uio);
+ if (error || tn < n)
+ break;
+ }
+
+ return (error);
+}
+
+int
+uscannerread(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uscanner_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev));
+ sc->sc_refcnt++;
+ error = uscanner_do_read(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+
+ return (error);
+}
+
+static int
+uscanner_do_write(struct uscanner_softc *sc, struct uio *uio, int flag)
+{
+ u_int32_t n;
+ int error = 0;
+ usbd_status err;
+
+ DPRINTFN(5, ("%s: uscanner_do_write\n", device_get_nameunit(sc->sc_dev)));
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ while ((n = min(sc->sc_bulkout_bufferlen, uio->uio_resid)) != 0) {
+ error = uiomove(sc->sc_bulkout_buffer, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("uscanner_do_write: transfer %d bytes\n", n));
+ err = usbd_bulk_transfer(
+ sc->sc_bulkout_xfer, sc->sc_bulkout_pipe,
+ 0, USBD_NO_TIMEOUT,
+ sc->sc_bulkout_buffer, &n,
+ "uscnwb");
+ if (err) {
+ if (err == USBD_INTERRUPTED)
+ error = EINTR;
+ else
+ error = EIO;
+ break;
+ }
+ }
+
+ return (error);
+}
+
+int
+uscannerwrite(struct cdev *dev, struct uio *uio, int flag)
+{
+ struct uscanner_softc *sc;
+ int error;
+
+ sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev));
+ sc->sc_refcnt++;
+ error = uscanner_do_write(sc, uio, flag);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(sc->sc_dev);
+ return (error);
+}
+
+static int
+uscanner_detach(device_t self)
+{
+ struct uscanner_softc *sc = device_get_softc(self);
+ int s;
+
+ DPRINTF(("uscanner_detach: sc=%p\n", sc));
+
+ sc->sc_dying = 1;
+ sc->sc_dev_flags = 0; /* make close really close device */
+
+ /* Abort all pipes. Causes processes waiting for transfer to wake. */
+ if (sc->sc_bulkin_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkin_pipe);
+ if (sc->sc_bulkout_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulkout_pipe);
+
+ s = splusb();
+ if (--sc->sc_refcnt >= 0) {
+ /* Wait for processes to go away. */
+ usb_detach_wait(sc->sc_dev);
+ }
+ splx(s);
+
+ /* destroy the device for the control endpoint */
+ destroy_dev(sc->dev);
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
+
+ return (0);
+}
+
+int
+uscannerpoll(struct cdev *dev, int events, struct thread *p)
+{
+ struct uscanner_softc *sc;
+ int revents = 0;
+
+ sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev));
+ if (sc->sc_dying)
+ return (EIO);
+
+ /*
+ * We have no easy way of determining if a read will
+ * yield any data or a write will happen.
+ * Pretend they will.
+ */
+ revents |= events &
+ (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM);
+
+ return (revents);
+}
+
+MODULE_DEPEND(uscanner, usb, 1, 1, 1);
+DRIVER_MODULE(uscanner, uhub, uscanner_driver, uscanner_devclass, usbd_driver_load, 0);
diff --git a/sys/legacy/dev/usb/uslcom.c b/sys/legacy/dev/usb/uslcom.c
new file mode 100644
index 0000000..edb13c5
--- /dev/null
+++ b/sys/legacy/dev/usb/uslcom.c
@@ -0,0 +1,419 @@
+/* $FreeBSD$ */
+/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/tty.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include "usbdevs.h"
+#include <dev/usb/ucomvar.h>
+
+#ifdef USLCOM_DEBUG
+#define DPRINTFN(n, x) do { if (uslcomdebug > (n)) printf x; } while (0)
+int uslcomdebug = 1;
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define USLCOMBUFSZ 256
+#define USLCOM_CONFIG_NO 0
+#define USLCOM_IFACE_NO 0
+
+#define USLCOM_SET_DATA_BITS(x) (x << 8)
+
+#define USLCOM_WRITE 0x41
+#define USLCOM_READ 0xc1
+
+#define USLCOM_UART 0x00
+#define USLCOM_BAUD_RATE 0x01
+#define USLCOM_DATA 0x03
+#define USLCOM_BREAK 0x05
+#define USLCOM_CTRL 0x07
+
+#define USLCOM_UART_DISABLE 0x00
+#define USLCOM_UART_ENABLE 0x01
+
+#define USLCOM_CTRL_DTR_ON 0x0001
+#define USLCOM_CTRL_DTR_SET 0x0100
+#define USLCOM_CTRL_RTS_ON 0x0002
+#define USLCOM_CTRL_RTS_SET 0x0200
+#define USLCOM_CTRL_CTS 0x0010
+#define USLCOM_CTRL_DSR 0x0020
+#define USLCOM_CTRL_DCD 0x0080
+
+
+#define USLCOM_BAUD_REF 0x384000
+
+#define USLCOM_STOP_BITS_1 0x00
+#define USLCOM_STOP_BITS_2 0x02
+
+#define USLCOM_PARITY_NONE 0x00
+#define USLCOM_PARITY_ODD 0x10
+#define USLCOM_PARITY_EVEN 0x20
+
+#define USLCOM_BREAK_OFF 0x00
+#define USLCOM_BREAK_ON 0x01
+
+
+struct uslcom_softc {
+ struct ucom_softc sc_ucom;
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+
+ u_char sc_msr;
+ u_char sc_lsr;
+
+ u_char sc_dying;
+};
+
+void uslcom_get_status(void *, int portno, u_char *lsr, u_char *msr);
+void uslcom_set(void *, int, int, int);
+int uslcom_param(void *, int, struct termios *);
+int uslcom_open(void *sc, int portno);
+void uslcom_close(void *, int);
+void uslcom_break(void *sc, int portno, int onoff);
+
+struct ucom_callback uslcom_callback = {
+ uslcom_get_status,
+ uslcom_set,
+ uslcom_param,
+ NULL,
+ uslcom_open,
+ uslcom_close,
+ NULL,
+ NULL,
+};
+
+static const struct usb_devno uslcom_devs[] = {
+ { USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER },
+ { USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD },
+ { USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128 },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102 },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2 },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO },
+ { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE },
+ { USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE },
+ { USB_VENDOR_USI, USB_PRODUCT_USI_MC60 }
+};
+
+static device_probe_t uslcom_match;
+static device_attach_t uslcom_attach;
+static device_detach_t uslcom_detach;
+
+static device_method_t uslcom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uslcom_match),
+ DEVMETHOD(device_attach, uslcom_attach),
+ DEVMETHOD(device_detach, uslcom_detach),
+ { 0, 0 }
+};
+
+static driver_t uslcom_driver = {
+ "ucom",
+ uslcom_methods,
+ sizeof (struct uslcom_softc)
+};
+
+DRIVER_MODULE(uslcom, uhub, uslcom_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uslcom, usb, 1, 1, 1);
+MODULE_DEPEND(uslcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(uslcom, 1);
+
+static int
+uslcom_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return UMATCH_NONE;
+
+ return (usb_lookup(uslcom_devs, uaa->vendor, uaa->product) != NULL) ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+}
+
+static int
+uslcom_attach(device_t self)
+{
+ struct uslcom_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ struct ucom_softc* ucom;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status error;
+ int i;
+
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ sc->sc_dev = self;
+ sc->sc_udev = uaa->device;
+
+ if (usbd_set_config_index(sc->sc_udev, USLCOM_CONFIG_NO, 1) != 0) {
+ device_printf(self, "could not set configuration no\n");
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ /* get the first interface handle */
+ error = usbd_device2interface_handle(sc->sc_udev, USLCOM_IFACE_NO,
+ &ucom->sc_iface);
+ if (error != 0) {
+ device_printf(self, "could not get interface handle\n");
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(self, "no endpoint descriptor found for %d\n",
+ i);
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ }
+
+ if (ucom->sc_bulkin_no == -1 || ucom->sc_bulkout_no == -1) {
+ device_printf(self, "missing endpoint\n");
+ sc->sc_dying = 1;
+ return ENXIO;
+ }
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = USLCOMBUFSZ;
+ ucom->sc_obufsize = USLCOMBUFSZ;
+ ucom->sc_ibufsizepad = USLCOMBUFSZ;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uslcom_callback;
+
+ DPRINTF(("uslcom: in = 0x%x, out = 0x%x\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no));
+
+ ucom_attach(&sc->sc_ucom);
+ return 0;
+}
+
+static int
+uslcom_detach(device_t self)
+{
+ struct uslcom_softc *sc = device_get_softc(self);
+
+ sc->sc_dying = 1;
+ return ucom_detach(&sc->sc_ucom);
+}
+
+int
+uslcom_open(void *vsc, int portno)
+{
+ struct uslcom_softc *sc = vsc;
+ usb_device_request_t req;
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_UART;
+ USETW(req.wValue, USLCOM_UART_ENABLE);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ return (0);
+}
+
+void
+uslcom_close(void *vsc, int portno)
+{
+ struct uslcom_softc *sc = vsc;
+ usb_device_request_t req;
+
+ if (sc->sc_dying)
+ return;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_UART;
+ USETW(req.wValue, USLCOM_UART_DISABLE);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ usbd_do_request(sc->sc_udev, &req, NULL);
+}
+
+void
+uslcom_set(void *vsc, int portno, int reg, int onoff)
+{
+ struct uslcom_softc *sc = vsc;
+ usb_device_request_t req;
+ int ctl;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
+ ctl |= USLCOM_CTRL_DTR_SET;
+ break;
+ case UCOM_SET_RTS:
+ ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
+ ctl |= USLCOM_CTRL_RTS_SET;
+ break;
+ case UCOM_SET_BREAK:
+ uslcom_break(sc, portno, onoff);
+ return;
+ default:
+ return;
+ }
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_CTRL;
+ USETW(req.wValue, ctl);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ usbd_do_request(sc->sc_udev, &req, NULL);
+}
+
+int
+uslcom_param(void *vsc, int portno, struct termios *t)
+{
+ struct uslcom_softc *sc = (struct uslcom_softc *)vsc;
+ usbd_status err;
+ usb_device_request_t req;
+ int data;
+
+ if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
+ return (EINVAL);
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_BAUD_RATE;
+ USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CSTOPB))
+ data = USLCOM_STOP_BITS_2;
+ else
+ data = USLCOM_STOP_BITS_1;
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ data |= USLCOM_PARITY_ODD;
+ else
+ data |= USLCOM_PARITY_EVEN;
+ } else
+ data |= USLCOM_PARITY_NONE;
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ data |= USLCOM_SET_DATA_BITS(5);
+ break;
+ case CS6:
+ data |= USLCOM_SET_DATA_BITS(6);
+ break;
+ case CS7:
+ data |= USLCOM_SET_DATA_BITS(7);
+ break;
+ case CS8:
+ data |= USLCOM_SET_DATA_BITS(8);
+ break;
+ }
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_DATA;
+ USETW(req.wValue, data);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ err = usbd_do_request(sc->sc_udev, &req, NULL);
+ if (err)
+ return (EIO);
+
+#if 0
+ /* XXX flow control */
+ if (ISSET(t->c_cflag, CRTSCTS))
+ /* rts/cts flow ctl */
+ } else if (ISSET(t->c_iflag, IXON|IXOFF)) {
+ /* xon/xoff flow ctl */
+ } else {
+ /* disable flow ctl */
+ }
+#endif
+
+ return (0);
+}
+
+void
+uslcom_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
+{
+ struct uslcom_softc *sc = vsc;
+
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+}
+
+void
+uslcom_break(void *vsc, int portno, int onoff)
+{
+ struct uslcom_softc *sc = vsc;
+ usb_device_request_t req;
+ int brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_BREAK;
+ USETW(req.wValue, brk);
+ USETW(req.wIndex, portno);
+ USETW(req.wLength, 0);
+ usbd_do_request(sc->sc_udev, &req, NULL);
+}
diff --git a/sys/legacy/dev/usb/uvisor.c b/sys/legacy/dev/usb/uvisor.c
new file mode 100644
index 0000000..6c4470e
--- /dev/null
+++ b/sys/legacy/dev/usb/uvisor.c
@@ -0,0 +1,639 @@
+/* $NetBSD: uvisor.c,v 1.9 2001/01/23 14:04:14 augustss Exp $ */
+/* $FreeBSD$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uvisor.c,v 1.12 2001/11/13 06:24:57 lukem Exp $
+ * $NetBSD: uvisor.c,v 1.13 2002/02/11 15:11:49 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.14 2002/02/27 23:00:03 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.15 2002/06/16 15:01:31 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.16 2002/07/11 21:14:36 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.17 2002/08/13 11:38:15 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.18 2003/02/05 00:50:14 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.19 2003/02/07 18:12:37 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.20 2003/04/11 01:30:10 simonb Exp $
+ */
+
+
+/*-
+ * Copyright (c) 2000 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.
+ */
+
+/*
+ * Handspring Visor (Palmpilot compatible PDA) driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/tty.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/ucomvar.h>
+
+#ifdef USB_DEBUG
+#define DPRINTF(x) if (uvisordebug) printf x
+#define DPRINTFN(n,x) if (uvisordebug>(n)) printf x
+int uvisordebug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor");
+SYSCTL_INT(_hw_usb_uvisor, OID_AUTO, debug, CTLFLAG_RW,
+ &uvisordebug, 0, "uvisor debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UVISOR_CONFIG_INDEX 0
+#define UVISOR_IFACE_INDEX 0
+#define UVISOR_MODVER 1
+
+/* From the Linux driver */
+/*
+ * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transfered to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ */
+#define UVISOR_REQUEST_BYTES_AVAILABLE 0x01
+
+/*
+ * UVISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ */
+#define UVISOR_CLOSE_NOTIFICATION 0x02
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ */
+#define UVISOR_GET_CONNECTION_INFORMATION 0x03
+
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ */
+#define UVISOR_MAX_CONN 8
+struct uvisor_connection_info {
+ uWord num_ports;
+ struct {
+ uByte port_function_id;
+ uByte port;
+ } connections[UVISOR_MAX_CONN];
+};
+#define UVISOR_CONNECTION_INFO_SIZE 18
+
+/* struct uvisor_connection_info.connection[x].port defines: */
+#define UVISOR_ENDPOINT_1 0x01
+#define UVISOR_ENDPOINT_2 0x02
+
+/* struct uvisor_connection_info.connection[x].port_function_id defines: */
+#define UVISOR_FUNCTION_GENERIC 0x00
+#define UVISOR_FUNCTION_DEBUGGER 0x01
+#define UVISOR_FUNCTION_HOTSYNC 0x02
+#define UVISOR_FUNCTION_CONSOLE 0x03
+#define UVISOR_FUNCTION_REMOTE_FILE_SYS 0x04
+
+/*
+ * Unknown PalmOS stuff.
+ */
+#define UVISOR_GET_PALM_INFORMATION 0x04
+#define UVISOR_GET_PALM_INFORMATION_LEN 0x44
+
+struct uvisor_palm_connection_info {
+ uByte num_ports;
+ uByte endpoint_numbers_different;
+ uWord reserved1;
+ struct {
+ uDWord port_function_id;
+ uByte port;
+ uByte end_point_info;
+ uWord reserved;
+ } connections[UVISOR_MAX_CONN];
+};
+
+
+/*
+ * Crank down UVISORBUFSIZE from 1024 to 64 to avoid a problem where
+ * the Palm device and the USB host controller deadlock. The USB host
+ * controller is expecting an early-end-of-transmission packet with 0
+ * data, and the Palm doesn't send one because it's already
+ * communicated the amount of data it's going to send in a header
+ * (which ucom/uvisor are oblivious to). This is the problem that has
+ * been known on the pilot-link lists as the "[Free]BSD USB problem",
+ * but not understood.
+ */
+#define UVISORIBUFSIZE 64
+#define UVISOROBUFSIZE 1024
+
+struct uvisor_softc {
+ struct ucom_softc sc_ucom;
+ u_int16_t sc_flags;
+};
+
+static usbd_status uvisor_init(struct uvisor_softc *);
+
+/*static usbd_status clie_3_5_init(struct uvisor_softc *);*/
+
+static void uvisor_close(void *, int);
+
+struct ucom_callback uvisor_callback = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ uvisor_close,
+ NULL,
+ NULL,
+};
+
+static device_probe_t uvisor_match;
+static device_attach_t uvisor_attach;
+static device_detach_t uvisor_detach;
+static device_method_t uvisor_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uvisor_match),
+ DEVMETHOD(device_attach, uvisor_attach),
+ DEVMETHOD(device_detach, uvisor_detach),
+ { 0, 0 }
+};
+
+
+static driver_t uvisor_driver = {
+ "ucom",
+ uvisor_methods,
+ sizeof (struct uvisor_softc)
+};
+
+DRIVER_MODULE(uvisor, uhub, uvisor_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uvisor, usb, 1, 1, 1);
+MODULE_DEPEND(uvisor, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(uvisor, UVISOR_MODVER);
+
+struct uvisor_type {
+ struct usb_devno uv_dev;
+ u_int16_t uv_flags;
+#define PALM4 0x0001
+#define VISOR 0x0002
+#define PALM35 0x0004
+};
+static const struct uvisor_type uvisor_devs[] = {
+ {{ USB_VENDOR_ACEECA, USB_PRODUCT_ACEECA_MEZ1000 }, PALM4 },
+ {{ USB_VENDOR_GARMIN, USB_PRODUCT_GARMIN_IQUE_3600 }, PALM4 },
+ {{ USB_VENDOR_FOSSIL, USB_PRODUCT_FOSSIL_WRISTPDA }, PALM4 },
+ {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR }, VISOR },
+ {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO }, PALM4 },
+ {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M500 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M505 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M515 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_I705 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M125 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M130 }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE }, PALM4 },
+ {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31 }, PALM4 },
+ {{ USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_I500 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40 }, 0 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60 }, PALM4 },
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35 }, PALM35 },
+/* {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25 }, PALM4 },*/
+/* {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TH55 }, PALM4 }, */ /* See PR 80935 */
+ {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TJ37 }, PALM4 },
+ {{ USB_VENDOR_TAPWAVE, USB_PRODUCT_TAPWAVE_ZODIAC }, PALM4 },
+};
+#define uvisor_lookup(v, p) ((const struct uvisor_type *)usb_lookup(uvisor_devs, v, p))
+
+
+static int
+uvisor_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ DPRINTFN(20,("uvisor: vendor=0x%x, product=0x%x\n",
+ uaa->vendor, uaa->product));
+
+ return (uvisor_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+static int
+uvisor_attach(device_t self)
+{
+ struct uvisor_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ usbd_interface_handle iface;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+ usbd_status err;
+ struct ucom_softc *ucom;
+
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ DPRINTFN(10,("\nuvisor_attach: sc=%p\n", sc));
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UVISOR_CONFIG_INDEX, 1);
+ if (err) {
+ device_printf(self, "failed to set configuration, err=%s\n",
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ err = usbd_device2interface_handle(dev, UVISOR_IFACE_INDEX, &iface);
+ if (err) {
+ device_printf(self, "failed to get interface, err=%s\n",
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ sc->sc_flags = uvisor_lookup(uaa->vendor, uaa->product)->uv_flags;
+
+ id = usbd_get_interface_descriptor(iface);
+
+ ucom->sc_udev = dev;
+ ucom->sc_iface = iface;
+
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ int addr, dir, attr;
+ ed = usbd_interface2endpoint_descriptor(iface, i);
+ if (ed == NULL) {
+ device_printf(self,
+ "could not read endpoint descriptor: %s\n",
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ addr = ed->bEndpointAddress;
+ dir = UE_GET_DIR(ed->bEndpointAddress);
+ attr = ed->bmAttributes & UE_XFERTYPE;
+ if (dir == UE_DIR_IN && attr == UE_BULK)
+ ucom->sc_bulkin_no = addr;
+ else if (dir == UE_DIR_OUT && attr == UE_BULK)
+ ucom->sc_bulkout_no = addr;
+ else {
+ device_printf(self, "unexpected endpoint\n");
+ goto bad;
+ }
+ }
+ if (ucom->sc_bulkin_no == -1) {
+ device_printf(self, "Could not find data bulk in\n");
+ goto bad;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ device_printf(self, "Could not find data bulk out\n");
+ goto bad;
+ }
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UVISORIBUFSIZE;
+ ucom->sc_obufsize = UVISOROBUFSIZE;
+ ucom->sc_ibufsizepad = UVISORIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uvisor_callback;
+
+#if 0
+ if (uaa->vendor == USB_VENDOR_SONY &&
+ uaa->product == USB_PRODUCT_SONY_CLIE_35)
+ err = clie_3_5_init(sc);
+ else
+#endif
+ err = uvisor_init(sc);
+
+ if (err) {
+ device_printf(ucom->sc_dev, "init failed, %s\n",
+ usbd_errstr(err));
+ goto bad;
+ }
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, ucom->sc_udev,
+ ucom->sc_dev);
+
+ DPRINTF(("uvisor: in=0x%x out=0x%x\n", ucom->sc_bulkin_no, ucom->sc_bulkout_no));
+ ucom_attach(&sc->sc_ucom);
+
+ return 0;
+
+bad:
+ DPRINTF(("uvisor_attach: ATTACH ERROR\n"));
+ ucom->sc_dying = 1;
+ return ENXIO;
+}
+
+#if 0
+
+int
+uvisor_activate(device_t self, enum devact act)
+{
+ struct uvisor_softc *sc = (struct uvisor_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_subdev != NULL)
+ rv = config_deactivate(sc->sc_subdev);
+ sc->sc_dying = 1;
+ break;
+ }
+ return (rv);
+}
+
+#endif
+
+static int
+uvisor_detach(device_t self)
+{
+ struct uvisor_softc *sc = device_get_softc(self);
+ int rv = 0;
+
+ DPRINTF(("uvisor_detach: sc=%p\n", sc));
+ sc->sc_ucom.sc_dying = 1;
+ rv = ucom_detach(&sc->sc_ucom);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_ucom.sc_udev,
+ sc->sc_ucom.sc_dev);
+
+ return (rv);
+}
+
+usbd_status
+uvisor_init(struct uvisor_softc *sc)
+{
+ usbd_status err;
+ usb_device_request_t req;
+ struct uvisor_connection_info coninfo;
+ struct uvisor_palm_connection_info pconinfo;
+ int actlen;
+ uWord avail;
+ char buffer[256];
+
+ if (sc->sc_flags & VISOR) {
+ DPRINTF(("uvisor_init: getting connection info\n"));
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_CONNECTION_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+ err = usbd_do_request_flags(sc->sc_ucom.sc_udev, &req, &coninfo,
+ USBD_SHORT_XFER_OK, &actlen,
+ USBD_DEFAULT_TIMEOUT);
+ if (err)
+ return (err);
+ }
+#ifdef USB_DEBUG
+ {
+ int i, np;
+ char *string;
+
+ np = UGETW(coninfo.num_ports);
+ device_printf(sc->sc_ucom.sc_dev, "Number of ports: %d\n", np);
+ for (i = 0; i < np; ++i) {
+ switch (coninfo.connections[i].port_function_id) {
+ case UVISOR_FUNCTION_GENERIC:
+ string = "Generic";
+ break;
+ case UVISOR_FUNCTION_DEBUGGER:
+ string = "Debugger";
+ break;
+ case UVISOR_FUNCTION_HOTSYNC:
+ string = "HotSync";
+ break;
+ case UVISOR_FUNCTION_REMOTE_FILE_SYS:
+ string = "Remote File System";
+ break;
+ default:
+ string = "unknown";
+ break;
+ }
+ device_printf(sc->sc_ucom.sc_dev,
+ "port %d, is for %s\n",
+ coninfo.connections[i].port, string);
+ }
+ }
+#endif
+
+ if (sc->sc_flags & PALM4) {
+ int port;
+ /* Palm OS 4.0 Hack */
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_PALM_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+ err = usbd_do_request_flags(sc->sc_ucom.sc_udev, &req, &pconinfo,
+ USBD_SHORT_XFER_OK, &actlen,
+ USBD_DEFAULT_TIMEOUT);
+ if (err)
+ return (err);
+
+ if (pconinfo.endpoint_numbers_different) {
+ port = pconinfo.connections[0].end_point_info;
+ sc->sc_ucom.sc_bulkin_no = (port >> 4) | UE_DIR_IN;
+ sc->sc_ucom.sc_bulkout_no = (port & 0xf) | UE_DIR_OUT;
+ } else {
+ port = pconinfo.connections[0].port;
+ sc->sc_ucom.sc_bulkin_no = port | UE_DIR_IN;
+ sc->sc_ucom.sc_bulkout_no = port | UE_DIR_OUT;
+ }
+#if 0
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_PALM_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+#endif
+ }
+
+ if (sc->sc_flags & PALM35) {
+ /* get the config number */
+ DPRINTF(("clie_3_5_init: getting config info\n"));
+ req.bmRequestType = UT_READ;
+ req.bRequest = UR_GET_CONFIG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+
+ /* get the interface number */
+ DPRINTF(("clie_3_5_init: get the interface number\n"));
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_INTERFACE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+ }
+
+ DPRINTF(("uvisor_init: getting available bytes\n"));
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 5);
+ USETW(req.wLength, sizeof avail);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, &avail);
+ if (err)
+ return (err);
+ DPRINTF(("uvisor_init: avail=%d\n", UGETW(avail)));
+
+ DPRINTF(("uvisor_init: done\n"));
+ return (err);
+}
+
+#if 0
+usbd_status
+clie_3_5_init(struct uvisor_softc *sc)
+{
+ usbd_status err;
+ usb_device_request_t req;
+ char buffer[256];
+
+ /*
+ * Note that PEG-300 series devices expect the following two calls.
+ */
+
+ /* get the config number */
+ DPRINTF(("clie_3_5_init: getting config info\n"));
+ req.bmRequestType = UT_READ;
+ req.bRequest = UR_GET_CONFIG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+
+ /* get the interface number */
+ DPRINTF(("clie_3_5_init: get the interface number\n"));
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_INTERFACE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, buffer);
+ if (err)
+ return (err);
+
+#ifdef USB_DEBUG
+ {
+ struct uvisor_connection_info coninfo;
+ int i, np;
+ char *string;
+
+ np = UGETW(coninfo.num_ports);
+ DPRINTF(("%s: Number of ports: %d\n", device_get_nameunit(sc->sc_ucom.sc_dev), np));
+ for (i = 0; i < np; ++i) {
+ switch (coninfo.connections[i].port_function_id) {
+ case UVISOR_FUNCTION_GENERIC:
+ string = "Generic";
+ break;
+ case UVISOR_FUNCTION_DEBUGGER:
+ string = "Debugger";
+ break;
+ case UVISOR_FUNCTION_HOTSYNC:
+ string = "HotSync";
+ break;
+ case UVISOR_FUNCTION_REMOTE_FILE_SYS:
+ string = "Remote File System";
+ break;
+ default:
+ string = "unknown";
+ break;
+ }
+ DPRINTF(("%s: port %d, is for %s\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), coninfo.connections[i].port,
+ string));
+ }
+ }
+#endif
+
+ DPRINTF(("clie_3_5_init: done\n"));
+ return (err);
+}
+#endif
+
+void
+uvisor_close(void *addr, int portno)
+{
+ struct uvisor_softc *sc = addr;
+ usb_device_request_t req;
+ struct uvisor_connection_info coninfo; /* XXX ? */
+ int actlen;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT; /* XXX read? */
+ req.bRequest = UVISOR_CLOSE_NOTIFICATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+ (void)usbd_do_request_flags(sc->sc_ucom.sc_udev, &req, &coninfo,
+ USBD_SHORT_XFER_OK, &actlen,
+ USBD_DEFAULT_TIMEOUT);
+}
diff --git a/sys/legacy/dev/usb/uvscom.c b/sys/legacy/dev/usb/uvscom.c
new file mode 100644
index 0000000..ac311f7
--- /dev/null
+++ b/sys/legacy/dev/usb/uvscom.c
@@ -0,0 +1,906 @@
+/* $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ */
+/*-
+ * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * uvscom: SUNTAC Slipper U VS-10U driver.
+ * Slipper U is a PC Card to USB converter for data communication card
+ * adapter. It supports DDI Pocket's Air H" C@rd, C@rd H" 64, NTT's P-in,
+ * P-in m@ater and various data communication card adapters.
+ */
+
+#include "opt_uvscom.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/serial.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/bus.h>
+#include <sys/ioccom.h>
+#include <sys/selinfo.h>
+#include <sys/proc.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+#include <dev/usb/usb_quirks.h>
+
+#include <dev/usb/ucomvar.h>
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom");
+#ifdef USB_DEBUG
+static int uvscomdebug = 0;
+SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RW,
+ &uvscomdebug, 0, "uvscom debug level");
+
+#define DPRINTFN(n, x) do { \
+ if (uvscomdebug > (n)) \
+ printf x; \
+ } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UVSCOM_MODVER 1 /* module version */
+
+#define UVSCOM_CONFIG_INDEX 0
+#define UVSCOM_IFACE_INDEX 0
+
+#ifndef UVSCOM_INTR_INTERVAL
+#define UVSCOM_INTR_INTERVAL 100 /* mS */
+#endif
+
+#define UVSCOM_UNIT_WAIT 5
+
+/* Request */
+#define UVSCOM_SET_SPEED 0x10
+#define UVSCOM_LINE_CTL 0x11
+#define UVSCOM_SET_PARAM 0x12
+#define UVSCOM_READ_STATUS 0xd0
+#define UVSCOM_SHUTDOWN 0xe0
+
+/* UVSCOM_SET_SPEED parameters */
+#define UVSCOM_SPEED_150BPS 0x00
+#define UVSCOM_SPEED_300BPS 0x01
+#define UVSCOM_SPEED_600BPS 0x02
+#define UVSCOM_SPEED_1200BPS 0x03
+#define UVSCOM_SPEED_2400BPS 0x04
+#define UVSCOM_SPEED_4800BPS 0x05
+#define UVSCOM_SPEED_9600BPS 0x06
+#define UVSCOM_SPEED_19200BPS 0x07
+#define UVSCOM_SPEED_38400BPS 0x08
+#define UVSCOM_SPEED_57600BPS 0x09
+#define UVSCOM_SPEED_115200BPS 0x0a
+
+/* UVSCOM_LINE_CTL parameters */
+#define UVSCOM_BREAK 0x40
+#define UVSCOM_RTS 0x02
+#define UVSCOM_DTR 0x01
+#define UVSCOM_LINE_INIT 0x08
+
+/* UVSCOM_SET_PARAM parameters */
+#define UVSCOM_DATA_MASK 0x03
+#define UVSCOM_DATA_BIT_8 0x03
+#define UVSCOM_DATA_BIT_7 0x02
+#define UVSCOM_DATA_BIT_6 0x01
+#define UVSCOM_DATA_BIT_5 0x00
+
+#define UVSCOM_STOP_MASK 0x04
+#define UVSCOM_STOP_BIT_2 0x04
+#define UVSCOM_STOP_BIT_1 0x00
+
+#define UVSCOM_PARITY_MASK 0x18
+#define UVSCOM_PARITY_EVEN 0x18
+#if 0
+#define UVSCOM_PARITY_UNK 0x10
+#endif
+#define UVSCOM_PARITY_ODD 0x08
+#define UVSCOM_PARITY_NONE 0x00
+
+/* Status bits */
+#define UVSCOM_TXRDY 0x04
+#define UVSCOM_RXRDY 0x01
+
+#define UVSCOM_DCD 0x08
+#define UVSCOM_NOCARD 0x04
+#define UVSCOM_DSR 0x02
+#define UVSCOM_CTS 0x01
+#define UVSCOM_USTAT_MASK (UVSCOM_NOCARD | UVSCOM_DSR | UVSCOM_CTS)
+
+struct uvscom_softc {
+ struct ucom_softc sc_ucom;
+
+ int sc_iface_number;/* interface number */
+
+ usbd_interface_handle sc_intr_iface; /* interrupt interface */
+ int sc_intr_number; /* interrupt number */
+ usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
+ u_char *sc_intr_buf; /* interrupt buffer */
+ int sc_isize;
+
+ u_char sc_dtr; /* current DTR state */
+ u_char sc_rts; /* current RTS state */
+
+ u_char sc_lsr; /* Local status register */
+ u_char sc_msr; /* uvscom status register */
+
+ uint16_t sc_lcr; /* Line control */
+ u_char sc_usr; /* unit status */
+
+ struct task sc_task;
+};
+
+/*
+ * These are the maximum number of bytes transferred per frame.
+ * The output buffer size cannot be increased due to the size encoding.
+ */
+#define UVSCOMIBUFSIZE 512
+#define UVSCOMOBUFSIZE 64
+
+#ifndef UVSCOM_DEFAULT_OPKTSIZE
+#define UVSCOM_DEFAULT_OPKTSIZE 8
+#endif
+
+static usbd_status uvscom_shutdown(struct uvscom_softc *);
+static usbd_status uvscom_reset(struct uvscom_softc *);
+static usbd_status uvscom_set_line_coding(struct uvscom_softc *,
+ uint16_t, uint16_t);
+static usbd_status uvscom_set_line(struct uvscom_softc *, uint16_t);
+static usbd_status uvscom_set_crtscts(struct uvscom_softc *);
+static void uvscom_get_status(void *, int, u_char *, u_char *);
+static void uvscom_dtr(struct uvscom_softc *, int);
+static void uvscom_rts(struct uvscom_softc *, int);
+static void uvscom_break(struct uvscom_softc *, int);
+
+static void uvscom_set(void *, int, int, int);
+static void uvscom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+static int uvscom_param(void *, int, struct termios *);
+static int uvscom_open(void *, int);
+static void uvscom_close(void *, int);
+static void uvscom_notify(void *, int);
+
+struct ucom_callback uvscom_callback = {
+ uvscom_get_status,
+ uvscom_set,
+ uvscom_param,
+ NULL,
+ uvscom_open,
+ uvscom_close,
+ NULL,
+ NULL
+};
+
+static const struct usb_devno uvscom_devs [] = {
+ /* SUNTAC U-Cable type A4 */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4 },
+ /* SUNTAC U-Cable type D2 */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L },
+ /* SUNTAC Ir-Trinity */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U },
+ /* SUNTAC U-Cable type P1 */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1 },
+ /* SUNTAC Slipper U */
+ { USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U },
+};
+#define uvscom_lookup(v, p) usb_lookup(uvscom_devs, v, p)
+
+static device_probe_t uvscom_match;
+static device_attach_t uvscom_attach;
+static device_detach_t uvscom_detach;
+
+static device_method_t uvscom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uvscom_match),
+ DEVMETHOD(device_attach, uvscom_attach),
+ DEVMETHOD(device_detach, uvscom_detach),
+ { 0, 0 }
+};
+
+static driver_t uvscom_driver = {
+ "ucom",
+ uvscom_methods,
+ sizeof (struct uvscom_softc)
+};
+
+DRIVER_MODULE(uvscom, uhub, uvscom_driver, ucom_devclass, usbd_driver_load, 0);
+MODULE_DEPEND(uvscom, usb, 1, 1, 1);
+MODULE_DEPEND(uvscom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
+MODULE_VERSION(uvscom, UVSCOM_MODVER);
+
+static int uvscomobufsiz = UVSCOM_DEFAULT_OPKTSIZE;
+static int uvscominterval = UVSCOM_INTR_INTERVAL;
+
+static int
+sysctl_hw_usb_uvscom_opktsize(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = uvscomobufsiz;
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+ if (0 < val && val <= UVSCOMOBUFSIZE)
+ uvscomobufsiz = val;
+ else
+ err = EINVAL;
+
+ return (err);
+}
+
+static int
+sysctl_hw_usb_uvscom_interval(SYSCTL_HANDLER_ARGS)
+{
+ int err, val;
+
+ val = uvscominterval;
+ err = sysctl_handle_int(oidp, &val, 0, req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+ if (0 < val && val <= 1000)
+ uvscominterval = val;
+ else
+ err = EINVAL;
+
+ return (err);
+}
+
+SYSCTL_PROC(_hw_usb_uvscom, OID_AUTO, opktsize, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_usb_uvscom_opktsize,
+ "I", "uvscom output packet size");
+SYSCTL_PROC(_hw_usb_uvscom, OID_AUTO, interval, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(int), sysctl_hw_usb_uvscom_interval,
+ "I", "uvscom interrpt pipe interval");
+
+static int
+uvscom_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ return (uvscom_lookup(uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+static int
+uvscom_attach(device_t self)
+{
+ struct uvscom_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_device_handle dev = uaa->device;
+ struct ucom_softc *ucom;
+ usb_config_descriptor_t *cdesc;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status err;
+ int i;
+
+ ucom = &sc->sc_ucom;
+ ucom->sc_dev = self;
+ ucom->sc_udev = dev;
+ ucom->sc_iface = uaa->iface;
+
+ DPRINTF(("uvscom attach: sc = %p\n", sc));
+
+ /* initialize endpoints */
+ ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
+ sc->sc_intr_number = -1;
+ sc->sc_intr_pipe = NULL;
+
+ /* Move the device into the configured state. */
+ err = usbd_set_config_index(dev, UVSCOM_CONFIG_INDEX, 1);
+ if (err) {
+ device_printf(self, "failed to set configuration, err=%s\n",
+ usbd_errstr(err));
+ goto error;
+ }
+
+ /* get the config descriptor */
+ cdesc = usbd_get_config_descriptor(ucom->sc_udev);
+
+ if (cdesc == NULL) {
+ device_printf(self, "failed to get configuration descriptor\n");
+ goto error;
+ }
+
+ /* get the common interface */
+ err = usbd_device2interface_handle(dev, UVSCOM_IFACE_INDEX,
+ &ucom->sc_iface);
+ if (err) {
+ device_printf(self, "failed to get interface, err=%s\n",
+ usbd_errstr(err));
+ goto error;
+ }
+
+ id = usbd_get_interface_descriptor(ucom->sc_iface);
+ sc->sc_iface_number = id->bInterfaceNumber;
+
+ /* Find endpoints */
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
+ if (ed == NULL) {
+ device_printf(self, "no endpoint descriptor for %d\n",
+ i);
+ goto error;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkin_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ ucom->sc_bulkout_no = ed->bEndpointAddress;
+ } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ sc->sc_intr_number = ed->bEndpointAddress;
+ sc->sc_isize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ if (ucom->sc_bulkin_no == -1) {
+ device_printf(self, "Could not find data bulk in\n");
+ goto error;
+ }
+ if (ucom->sc_bulkout_no == -1) {
+ device_printf(self, "Could not find data bulk out\n");
+ goto error;
+ }
+ if (sc->sc_intr_number == -1) {
+ device_printf(self, "Could not find interrupt in\n");
+ goto error;
+ }
+
+ sc->sc_dtr = sc->sc_rts = 0;
+ sc->sc_lcr = UVSCOM_LINE_INIT;
+
+ ucom->sc_parent = sc;
+ ucom->sc_portno = UCOM_UNK_PORTNO;
+ /* bulkin, bulkout set above */
+ ucom->sc_ibufsize = UVSCOMIBUFSIZE;
+ ucom->sc_obufsize = uvscomobufsiz;
+ ucom->sc_ibufsizepad = UVSCOMIBUFSIZE;
+ ucom->sc_opkthdrlen = 0;
+ ucom->sc_callback = &uvscom_callback;
+
+ err = uvscom_reset(sc);
+
+ if (err) {
+ device_printf(self, "reset failed, %s\n", usbd_errstr(err));
+ goto error;
+ }
+
+ DPRINTF(("uvscom: in = 0x%x out = 0x%x intr = 0x%x\n",
+ ucom->sc_bulkin_no, ucom->sc_bulkout_no, sc->sc_intr_number));
+
+ TASK_INIT(&sc->sc_task, 0, uvscom_notify, sc);
+ ucom_attach(&sc->sc_ucom);
+ return 0;
+
+error:
+ ucom->sc_dying = 1;
+ return ENXIO;
+}
+
+static int
+uvscom_detach(device_t self)
+{
+ struct uvscom_softc *sc = device_get_softc(self);
+ int rv = 0;
+
+ DPRINTF(("uvscom_detach: sc = %p\n", sc));
+
+ sc->sc_ucom.sc_dying = 1;
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ rv = ucom_detach(&sc->sc_ucom);
+
+ return (rv);
+}
+
+static usbd_status
+uvscom_readstat(struct uvscom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ uint16_t r;
+
+ DPRINTF(("%s: send readstat\n", device_get_nameunit(sc->sc_ucom.sc_dev)));
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_READ_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 2);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, &r);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "uvscom_readstat: %s\n",
+ usbd_errstr(err));
+ return (err);
+ }
+
+ DPRINTF(("%s: uvscom_readstat: r = %d\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), r));
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static usbd_status
+uvscom_shutdown(struct uvscom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("%s: send shutdown\n", device_get_nameunit(sc->sc_ucom.sc_dev)));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_SHUTDOWN;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "uvscom_shutdown: %s\n",
+ usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static usbd_status
+uvscom_reset(struct uvscom_softc *sc)
+{
+ DPRINTF(("%s: uvscom_reset\n", device_get_nameunit(sc->sc_ucom.sc_dev)));
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static usbd_status
+uvscom_set_crtscts(struct uvscom_softc *sc)
+{
+ DPRINTF(("%s: uvscom_set_crtscts\n", device_get_nameunit(sc->sc_ucom.sc_dev)));
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static usbd_status
+uvscom_set_line(struct uvscom_softc *sc, uint16_t line)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("%s: uvscom_set_line: %04x\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), line));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_LINE_CTL;
+ USETW(req.wValue, line);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "uvscom_set_line: %s\n",
+ usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static usbd_status
+uvscom_set_line_coding(struct uvscom_softc *sc, uint16_t lsp, uint16_t ls)
+{
+ usb_device_request_t req;
+ usbd_status err;
+
+ DPRINTF(("%s: uvscom_set_line_coding: %02x %02x\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), lsp, ls));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_SET_SPEED;
+ USETW(req.wValue, lsp);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "uvscom_set_line_coding: %s\n",
+ usbd_errstr(err));
+ return (err);
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_SET_PARAM;
+ USETW(req.wValue, ls);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev, "uvscom_set_line_coding: %s\n",
+ usbd_errstr(err));
+ return (err);
+ }
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+static void
+uvscom_dtr(struct uvscom_softc *sc, int onoff)
+{
+ DPRINTF(("%s: uvscom_dtr: onoff = %d\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), onoff));
+
+ if (sc->sc_dtr == onoff)
+ return; /* no change */
+
+ sc->sc_dtr = onoff;
+
+ if (onoff)
+ SET(sc->sc_lcr, UVSCOM_DTR);
+ else
+ CLR(sc->sc_lcr, UVSCOM_DTR);
+
+ uvscom_set_line(sc, sc->sc_lcr);
+}
+
+static void
+uvscom_rts(struct uvscom_softc *sc, int onoff)
+{
+ DPRINTF(("%s: uvscom_rts: onoff = %d\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), onoff));
+
+ if (sc->sc_rts == onoff)
+ return; /* no change */
+
+ sc->sc_rts = onoff;
+
+ if (onoff)
+ SET(sc->sc_lcr, UVSCOM_RTS);
+ else
+ CLR(sc->sc_lcr, UVSCOM_RTS);
+
+ uvscom_set_line(sc, sc->sc_lcr);
+}
+
+static void
+uvscom_break(struct uvscom_softc *sc, int onoff)
+{
+ DPRINTF(("%s: uvscom_break: onoff = %d\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), onoff));
+
+ if (onoff)
+ uvscom_set_line(sc, SET(sc->sc_lcr, UVSCOM_BREAK));
+}
+
+static void
+uvscom_set(void *addr, int portno, int reg, int onoff)
+{
+ struct uvscom_softc *sc = addr;
+
+ switch (reg) {
+ case UCOM_SET_DTR:
+ uvscom_dtr(sc, onoff);
+ break;
+ case UCOM_SET_RTS:
+ uvscom_rts(sc, onoff);
+ break;
+ case UCOM_SET_BREAK:
+ uvscom_break(sc, onoff);
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+uvscom_param(void *addr, int portno, struct termios *t)
+{
+ struct uvscom_softc *sc = addr;
+ usbd_status err;
+ uint16_t lsp;
+ uint16_t ls;
+
+ DPRINTF(("%s: uvscom_param: sc = %p\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), sc));
+
+ ls = 0;
+
+ switch (t->c_ospeed) {
+ case B150:
+ lsp = UVSCOM_SPEED_150BPS;
+ break;
+ case B300:
+ lsp = UVSCOM_SPEED_300BPS;
+ break;
+ case B600:
+ lsp = UVSCOM_SPEED_600BPS;
+ break;
+ case B1200:
+ lsp = UVSCOM_SPEED_1200BPS;
+ break;
+ case B2400:
+ lsp = UVSCOM_SPEED_2400BPS;
+ break;
+ case B4800:
+ lsp = UVSCOM_SPEED_4800BPS;
+ break;
+ case B9600:
+ lsp = UVSCOM_SPEED_9600BPS;
+ break;
+ case B19200:
+ lsp = UVSCOM_SPEED_19200BPS;
+ break;
+ case B38400:
+ lsp = UVSCOM_SPEED_38400BPS;
+ break;
+ case B57600:
+ lsp = UVSCOM_SPEED_57600BPS;
+ break;
+ case B115200:
+ lsp = UVSCOM_SPEED_115200BPS;
+ break;
+ default:
+ return (EIO);
+ }
+
+ if (ISSET(t->c_cflag, CSTOPB))
+ SET(ls, UVSCOM_STOP_BIT_2);
+ else
+ SET(ls, UVSCOM_STOP_BIT_1);
+
+ if (ISSET(t->c_cflag, PARENB)) {
+ if (ISSET(t->c_cflag, PARODD))
+ SET(ls, UVSCOM_PARITY_ODD);
+ else
+ SET(ls, UVSCOM_PARITY_EVEN);
+ } else
+ SET(ls, UVSCOM_PARITY_NONE);
+
+ switch (ISSET(t->c_cflag, CSIZE)) {
+ case CS5:
+ SET(ls, UVSCOM_DATA_BIT_5);
+ break;
+ case CS6:
+ SET(ls, UVSCOM_DATA_BIT_6);
+ break;
+ case CS7:
+ SET(ls, UVSCOM_DATA_BIT_7);
+ break;
+ case CS8:
+ SET(ls, UVSCOM_DATA_BIT_8);
+ break;
+ default:
+ return (EIO);
+ }
+
+ err = uvscom_set_line_coding(sc, lsp, ls);
+ if (err)
+ return (EIO);
+
+ if (ISSET(t->c_cflag, CRTSCTS)) {
+ err = uvscom_set_crtscts(sc);
+ if (err)
+ return (EIO);
+ }
+
+ return (0);
+}
+
+static int
+uvscom_open(void *addr, int portno)
+{
+ struct uvscom_softc *sc = addr;
+ int err;
+ int i;
+
+ if (sc->sc_ucom.sc_dying)
+ return (ENXIO);
+
+ DPRINTF(("uvscom_open: sc = %p\n", sc));
+
+ /* change output packet size */
+ sc->sc_ucom.sc_obufsize = uvscomobufsiz;
+
+ if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
+ DPRINTF(("uvscom_open: open interrupt pipe.\n"));
+
+ sc->sc_usr = 0; /* clear unit status */
+
+ err = uvscom_readstat(sc);
+ if (err) {
+ DPRINTF(("%s: uvscom_open: readstat faild\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev)));
+ return (ENXIO);
+ }
+
+ sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
+ err = usbd_open_pipe_intr(sc->sc_ucom.sc_iface,
+ sc->sc_intr_number,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_intr_pipe,
+ sc,
+ sc->sc_intr_buf,
+ sc->sc_isize,
+ uvscom_intr,
+ uvscominterval);
+ if (err) {
+ device_printf(sc->sc_ucom.sc_dev,
+ "cannot open interrupt pipe (addr %d)\n",
+ sc->sc_intr_number);
+ return (ENXIO);
+ }
+ } else {
+ DPRINTF(("uvscom_open: did not open interrupt pipe.\n"));
+ }
+
+ if ((sc->sc_usr & UVSCOM_USTAT_MASK) == 0) {
+ /* unit is not ready */
+
+ for (i = UVSCOM_UNIT_WAIT; i > 0; --i) {
+ pause("uvsop", hz); /* XXX */
+ if (ISSET(sc->sc_usr, UVSCOM_USTAT_MASK))
+ break;
+ }
+ if (i == 0) {
+ DPRINTF(("%s: unit is not ready\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev)));
+ return (ENXIO);
+ }
+
+ /* check PC Card was inserted */
+ if (ISSET(sc->sc_usr, UVSCOM_NOCARD)) {
+ DPRINTF(("%s: no card\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev)));
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static void
+uvscom_close(void *addr, int portno)
+{
+ struct uvscom_softc *sc = addr;
+ int err;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ DPRINTF(("uvscom_close: close\n"));
+
+ uvscom_shutdown(sc);
+
+ if (sc->sc_intr_pipe != NULL) {
+ err = usbd_abort_pipe(sc->sc_intr_pipe);
+ if (err)
+ device_printf(sc->sc_ucom.sc_dev,
+ "abort interrupt pipe failed: %s\n",
+ usbd_errstr(err));
+ err = usbd_close_pipe(sc->sc_intr_pipe);
+ if (err)
+ device_printf(sc->sc_ucom.sc_dev,
+ "close interrupt pipe failed: %s\n",
+ usbd_errstr(err));
+ free(sc->sc_intr_buf, M_USBDEV);
+ sc->sc_intr_pipe = NULL;
+ }
+}
+
+static void
+uvscom_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct uvscom_softc *sc = priv;
+ u_char *buf = sc->sc_intr_buf;
+ u_char pstatus;
+
+ if (sc->sc_ucom.sc_dying)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ device_printf(sc->sc_ucom.sc_dev,
+ "uvscom_intr: abnormal status: %s\n",
+ usbd_errstr(status));
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+ return;
+ }
+
+ DPRINTFN(2, ("%s: uvscom status = %02x %02x\n",
+ device_get_nameunit(sc->sc_ucom.sc_dev), buf[0], buf[1]));
+
+ sc->sc_lsr = sc->sc_msr = 0;
+ sc->sc_usr = buf[1];
+
+ pstatus = buf[0];
+ if (ISSET(pstatus, UVSCOM_TXRDY))
+ SET(sc->sc_lsr, ULSR_TXRDY);
+ if (ISSET(pstatus, UVSCOM_RXRDY))
+ SET(sc->sc_lsr, ULSR_RXRDY);
+
+ pstatus = buf[1];
+ if (ISSET(pstatus, UVSCOM_CTS))
+ SET(sc->sc_msr, SER_CTS);
+ if (ISSET(pstatus, UVSCOM_DSR))
+ SET(sc->sc_msr, SER_DSR);
+ if (ISSET(pstatus, UVSCOM_DCD))
+ SET(sc->sc_msr, SER_DCD);
+
+ /* Deferred notifying to the ucom layer */
+ taskqueue_enqueue(taskqueue_swi_giant, &sc->sc_task);
+}
+
+static void
+uvscom_notify(void *arg, int count)
+{
+ struct uvscom_softc *sc;
+
+ sc = (struct uvscom_softc *)arg;
+ if (sc->sc_ucom.sc_dying)
+ return;
+ ucom_status_change(&sc->sc_ucom);
+}
+
+static void
+uvscom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct uvscom_softc *sc = addr;
+
+ if (lsr != NULL)
+ *lsr = sc->sc_lsr;
+ if (msr != NULL)
+ *msr = sc->sc_msr;
+}
diff --git a/sys/legacy/dev/usb/uxb360gp_rdesc.h b/sys/legacy/dev/usb/uxb360gp_rdesc.h
new file mode 100644
index 0000000..b5a43f9
--- /dev/null
+++ b/sys/legacy/dev/usb/uxb360gp_rdesc.h
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2005 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * The descriptor has no output report format, thus preventing you from
+ * controlling the LEDs and the built-in rumblers.
+ */
+static const uByte uhid_xb360gp_report_descr[] = {
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+ 0x09, 0x05, /* USAGE (Gamepad) */
+ 0xa1, 0x01, /* COLLECTION (Application) */
+ /* Unused */
+ 0x75, 0x08, /* REPORT SIZE (8) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x81, 0x01, /* INPUT (Constant) */
+ /* Byte count */
+ 0x75, 0x08, /* REPORT SIZE (8) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+ 0x09, 0x3b, /* USAGE (Byte Count) */
+ 0x81, 0x01, /* INPUT (Constant) */
+ /* D-Pad */
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+ 0x09, 0x01, /* USAGE (Pointer) */
+ 0xa1, 0x00, /* COLLECTION (Physical) */
+ 0x75, 0x01, /* REPORT SIZE (1) */
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */
+ 0x95, 0x04, /* REPORT COUNT (4) */
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+ 0x09, 0x90, /* USAGE (D-Pad Up) */
+ 0x09, 0x91, /* USAGE (D-Pad Down) */
+ 0x09, 0x93, /* USAGE (D-Pad Left) */
+ 0x09, 0x92, /* USAGE (D-Pad Right) */
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */
+ 0xc0, /* END COLLECTION */
+ /* Buttons 5-11 */
+ 0x75, 0x01, /* REPORT SIZE (1) */
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */
+ 0x95, 0x07, /* REPORT COUNT (7) */
+ 0x05, 0x09, /* USAGE PAGE (Button) */
+ 0x09, 0x08, /* USAGE (Button 8) */
+ 0x09, 0x07, /* USAGE (Button 7) */
+ 0x09, 0x09, /* USAGE (Button 9) */
+ 0x09, 0x0a, /* USAGE (Button 10) */
+ 0x09, 0x05, /* USAGE (Button 5) */
+ 0x09, 0x06, /* USAGE (Button 6) */
+ 0x09, 0x0b, /* USAGE (Button 11) */
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */
+ /* Unused */
+ 0x75, 0x01, /* REPORT SIZE (1) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x81, 0x01, /* INPUT (Constant) */
+ /* Buttons 1-4 */
+ 0x75, 0x01, /* REPORT SIZE (1) */
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */
+ 0x95, 0x04, /* REPORT COUNT (4) */
+ 0x05, 0x09, /* USAGE PAGE (Button) */
+ 0x19, 0x01, /* USAGE MINIMUM (Button 1) */
+ 0x29, 0x04, /* USAGE MAXIMUM (Button 4) */
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */
+ /* Triggers */
+ 0x75, 0x08, /* REPORT SIZE (8) */
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */
+ 0x26, 0xff, 0x00, /* LOGICAL MAXIMUM (255) */
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */
+ 0x46, 0xff, 0x00, /* PHYSICAL MAXIMUM (255) */
+ 0x95, 0x02, /* REPORT SIZE (2) */
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+ 0x09, 0x32, /* USAGE (Z) */
+ 0x09, 0x35, /* USAGE (Rz) */
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */
+ /* Sticks */
+ 0x75, 0x10, /* REPORT SIZE (16) */
+ 0x16, 0x00, 0x80, /* LOGICAL MINIMUM (-32768) */
+ 0x26, 0xff, 0x7f, /* LOGICAL MAXIMUM (32767) */
+ 0x36, 0x00, 0x80, /* PHYSICAL MINIMUM (-32768) */
+ 0x46, 0xff, 0x7f, /* PHYSICAL MAXIMUM (32767) */
+ 0x95, 0x04, /* REPORT COUNT (4) */
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x09, 0x33, /* USAGE (Rx) */
+ 0x09, 0x34, /* USAGE (Ry) */
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */
+ /* Unused */
+ 0x75, 0x30, /* REPORT SIZE (48) */
+ 0x95, 0x01, /* REPORT COUNT (1) */
+ 0x81, 0x01, /* INPUT (Constant) */
+ 0xc0, /* END COLLECTION */
+};
OpenPOWER on IntegriCloud